Trygghet, Trivsel och Studiero

Sigtuna kommuns skolenkät

Author
Affiliation
Magnus Johansson
Published

August 29, 2023

1 Importera data

Code
df.all <- read.spss("~/RISE/SHIC - Data i Dialog - Data i Dialog/data/Sigtuna/TTS/Databas Sigtuna TTS.sav", to.data.frame = TRUE)

df <- df.all

spssLabels <- df %>% 
  attr('variable.labels') %>% 
  as.data.frame()

names(df) <- spssLabels$.

itemlabels <- data.frame(itemnr = paste0("q",c(1:34)),
                         item = names(df[8:41])
                         ) %>% 
  mutate(item = str_replace(item, "kännt", "känt"))
write.csv(itemlabels, "TTSitemlabels.csv")

names(df)[8:41] <- paste0("q",c(1:34))

df <- df %>%
  mutaterskurs = str_replace(Årskurs, "Elever ", "")) %>%
  mutate(
    Årskurs = str_trim(Årskurs, side = "right"),
    Klass = str_trim(Klass, side = "right"),
    Skola = str_trim(`Skolans namn`, side = "right"), .after = "Kommun"
  ) %>%
  select(!`Skolans namn`) %>%
  mutate(Stadium = case_when(
    Årskurs %in% c("Åk 1", "Åk 2", "Åk 3") ~ "Lågstadiet",
    Årskurs %in% c("Åk 4", "Åk 5", "Åk 6") ~ "Mellanstadiet",
    Årskurs %in% c("Åk 7", "Åk 8", "Åk 9") ~ "Högstadiet",
    Årskurs == "Förskoleklass" ~ "Förskoleklass",
    TRUE ~ NA
  ), .before = "Årskurs") %>%
  mutate(
    Årskurs = factor(Årskurs),
    Skola = factor(Skola),
    Klass = factor(Klass),
    Stadium = factor(Stadium)
  ) %>%
  mutate(Kön = car::recode(Kön, "'[Ej svar]'=NA"))

årtal <- c(2017:2022)
årskurser <- df %>% 
  distinct(Årskurs) %>% 
  pull(Årskurs)

df.items <- df %>% 
  select(starts_with("q"))

2 Deskriptiva data

Code
df %>% 
  group_by(År,Stadium) %>% 
  reframe(Antal = n()) %>% 
  ggplot(aes(x = År, y = Antal, color = Stadium)) + 
  geom_line(linewidth = 1) +
  geom_point(size = 3) +
  #scale_color_viridis_d() +
  scale_x_continuous(guide = guide_axis(n.dodge = 1), breaks = årtal) +
  scale_y_continuous(limits = c(0,NA)) +
  labs(title = "Antal respondenter per år",
       subtitle = "Fördelat på stadium") +
  theme_minimal() +
  theme_rise() +
  theme(text = element_text(family = "Lato"))

Code
df %>%
  group_by(År,Årskurs) %>% 
  reframe(Antal = n()) %>% 
  ggplot(aes(x = År, y = Antal, color = Årskurs)) + 
  geom_line(linewidth = 1) +
  geom_point(size = 3) +
  #scale_color_viridis_d() +
  scale_x_continuous(guide = guide_axis(n.dodge = 1), breaks = årtal) +
  scale_y_continuous(limits = c(0,NA)) +
  labs(title = "Antal respondenter per år",
       subtitle = "Fördelat på årskurs") +
  theme_minimal() +
  theme_rise() +
  theme(text = element_text(family = "Lato"))

Code
df %>% 
  drop_na(Kön) %>% 
  group_by(År,Stadium,Kön) %>% 
  reframe(Antal = n()) %>% 
  ggplot(aes(x = År, y = Antal, color = Stadium)) + 
  geom_line(linewidth = 1) +
  geom_point(size = 3) +
  #scale_color_viridis_d() +
  scale_x_continuous(guide = guide_axis(n.dodge = 1), breaks = årtal) +
  scale_y_continuous(limits = c(0,NA)) +
  labs(title = "Antal respondenter per år",
       subtitle = "Fördelat på stadium och kön") +
  theme_minimal() +
  theme_rise() +
  theme(text = element_text(family = "Lato")) +
  facet_wrap(~Kön)

3 Frågor i enkäten

Code
itemlabels %>% 
  slice(c(1:10,14:15,27:30,32:34)) %>% 
  kbl_rise()
itemnr item
q1 Jag trivs i skolan
q2 Jag trivs i min klass
q3 Jag kan koncentrera mig på lektionerna
q4 Jag kan jobba på lektionerna
q5 Jag lär mig olika saker på lektionerna
q6 Jag känner mig trygg i skolan
q7 Jag blir bra bemött av vuxna i skolan
q8 Jag blir bra bemött av elever i skolan
q9 De vuxna på skolan reagerar om de får reda på att någon har varit elak mot en elev
q10 Jag vet vem på skolan jag kan vända mig till om någon är elak mot mig
q14 Jag upplever att elevrådet har möjlighet att vara delaktigt i beslut
q15 Jag känner till FN:s barnkonvention och mina rättigheter enligt den
q27 Jag trivs på fritidshemmet
q28 Jag känner mig trygg på fritidshemmet
q29 Jag får vara med och påverka vad vi gör på fritidshemmet
q30 Det jag lär mig på fritidshemmet hjälper mig i skolarbetet
q32 Jag lär och utvecklar mig på modersmålsundervisningen
q33 Jag får hjälp av min modersmålslärare när jag behöver den
q34 Jag kan koncentrera mig på modersmålsundervisningen

Ovanstående frågor har fyra svarskategorier, där bara ändpunkterna har etiketter (Instämmer helt - Instämmer inte alls), samt alternativet “Vet ej”.

Fråga 11-12 kan besvaras med Ja/Nej.

  • q11: Har du varit utsatt för kränkning på din skola i år?
  • q12: Har du varit utsatt för upprepade kränkningar på din skola i år?
Code
itemlabels %>% 
  slice(c(27:30,32:34)) %>% 
  kbl_rise()
itemnr item
q27 Jag trivs på fritidshemmet
q28 Jag känner mig trygg på fritidshemmet
q29 Jag får vara med och påverka vad vi gör på fritidshemmet
q30 Det jag lär mig på fritidshemmet hjälper mig i skolarbetet
q32 Jag lär och utvecklar mig på modersmålsundervisningen
q33 Jag får hjälp av min modersmålslärare när jag behöver den
q34 Jag kan koncentrera mig på modersmålsundervisningen

Fråga 27-30 handlar om fritidshemmet, medan 31-34 handlar om modersmålsundervisning.

Fråga 13 är “Var känner du dig minst trygg på skolan?”, med följande åtta möjliga svarsalternativ:

Code
df %>% distinct(q13) %>% 
  filter(!q13 == "NA") %>% 
  rename(`Var känner du dig minst trygg på skolan?` = q13) %>% 
  kbl_rise()
Var känner du dig minst trygg på skolan?
Jag känner mig trygg överallt på skolan
Korridoren
Omklädningsrummet
Skolgården
Klassrummet
Annat:
Matsalen
Internet/sociala medier

Frågorna 16-22 handlar om de sju diskrimineringsgrunderna, och fråga 23 blir svårtolkad. Ska man svara “Ja” på den om man blivit illa behandlad på grund av något annat än de sju diskrimineringsgrunderna, eller om man inte alls blivit illa behandlad? Vad betyder “Nej” på den frågan?

Fråga 24 och 25 handlar om utsatthet för sexuella trakasserier någon gång under året (oavsett plats) samt om det hänt upprepade gånger på skolan (Ja/nej/vet ej).

Code
itemlabels %>% 
  slice(c(16:23)) %>% 
  kbl_rise()
itemnr item
q16 Har du känt dig illa/annorlunda behandlad på din skola i år på grund av: - Ditt kön
q17 Har du känt dig illa/annorlunda behandlad på din skola i år på grund av: - Din etniska bakgrund
q18 Har du känt dig illa/annorlunda behandlad på din skola i år på grund av: - Din religion eller trosuppfattning
q19 Har du känt dig illa/annorlunda behandlad på din skola i år på grund av: - Ditt funktionshinder
q20 Har du känt dig illa/annorlunda behandlad på din skola i år på grund av: - Din sexuella läggning
q21 Har du känt dig illa/annorlunda behandlad på din skola i år på grund av: - Din ålder
q22 Har du känt dig illa/annorlunda behandlad på din skola i år på grund av: - Din könsidentitet/ könsuttryck
q23 Har du känt dig illa/annorlunda behandlad på din skola i år på grund av: - Inget av ovanstående

4 Explorativ analys

Vi lägger in fråga 1-12 i en analys för att se om de kan användas tillsammans för att estimera indexvärden. Initialt tittar vi enbart på data från högstadiet.

Code
df %>% 
  filter(Stadium == "Högstadiet") %>% 
  pull(Kön) %>% 
  RIdemographics(.,"Kön")
df %>% 
  filter(Stadium == "Högstadiet") %>% 
  pull(Årskurs) %>% 
  RIdemographics(.,"Årskurs")
df %>% 
  filter(Stadium == "Högstadiet") %>% 
  pull(År) %>% 
  RIdemographics(.,"År")
Kön n Percent
Annat/Vill ej svara 192 3.0
Flicka 3051 47.9
Pojke 3130 49.1
Årskurs n Percent
Åk 1 0 0.0
Åk 2 0 0.0
Åk 3 0 0.0
Åk 4 0 0.0
Åk 5 0 0.0
Åk 6 0 0.0
Åk 7 2231 35.0
Åk 8 2062 32.4
Åk 9 2080 32.6
Förskoleklass 0 0.0
År n Percent
2017 871 13.7
2018 981 15.4
2019 1135 17.8
2020 1270 19.9
2021 1046 16.4
2022 1070 16.8

Först tittar vi på rådata.

Code
library(skimr)
df %>% 
  filter(Stadium == "Högstadiet") %>% 
  select(q1:q12) %>%
  skim()
Data summary
Name Piped data
Number of rows 6373
Number of columns 12
_______________________
Column type frequency:
factor 12
________________________
Group variables None

Variable type: factor

skim_variable n_missing complete_rate ordered n_unique top_counts
q1 0 1.00 FALSE 5 Ins: 2770, 3: 2496, 2: 664, Ins: 251
q2 871 0.86 FALSE 5 Ins: 2781, 3: 1772, 2: 587, Ins: 231
q3 0 1.00 FALSE 5 3: 2947, 2: 1801, Ins: 960, Ins: 523
q4 871 0.86 FALSE 5 3: 2727, Ins: 1478, 2: 965, Ins: 243
q5 871 0.86 FALSE 5 3: 2491, Ins: 1703, 2: 987, Ins: 229
q6 0 1.00 FALSE 5 Ins: 2907, 3: 2312, 2: 744, Ins: 260
q7 0 1.00 FALSE 5 Ins: 2927, 3: 2199, 2: 803, Ins: 290
q8 0 1.00 FALSE 5 3: 2692, Ins: 2246, 2: 991, Ins: 264
q9 871 0.86 FALSE 5 3: 1676, Ins: 1663, 2: 1036, Vet: 599
q10 0 1.00 FALSE 5 Ins: 3367, 3: 1549, 2: 700, Ins: 488
q11 0 1.00 FALSE 2 Nej: 5086, Ja: 1287
q12 0 1.00 FALSE 2 Nej: 5676, Ja: 697

4.1 Missing data

Code
df %>% 
  filter(Stadium == "Högstadiet") %>% 
  select(q1:q12) %>%
  RImissing()

Fyra items har exakt samma struktur. Det antyder dels att data är “tvättade” redan, och att det rör sig om items som tillkommit/tagits bort vid någon datainsamling.

Låt oss titta på andelen missing data (NA) per år.

Code
df %>% 
  select(q1:q12,År,Kön) %>% 
  #filter(År >= 2013) %>% 
  drop_na(Kön) %>% 
  pivot_longer(q1:q12, names_to = "Item", values_to = "Svarskategori") %>% 
  group_by(År,Kön,Item) %>% 
  count(Svarskategori) %>% 
  mutate(Andel = 100 * n / sum(n)) %>% 
  #filter(Svarskategori %in% c("Vet inte",NA)) %>% 
  ggplot(aes(x = factor(År), y = Andel, group = Kön, color = Kön, fill = Kön)) +
  geom_point(size = 1.2, alpha = 0.85) +
  geom_line(linewidth = 0.4, alpha = 0.85) +
  #scale_y_continuous(limits = c(0,100), breaks = c(0,20,40,60,80,100)) +
  scale_color_manual(values = RISEpalette1)  +
  scale_x_discrete(guide = guide_axis(n.dodge = 2)) +
    labs(title = "Fördelning av svar items q1-q12",
       subtitle = "Samtliga årskurser") +
  ylab("Andel respondenter i procent") +
  xlab("Årtal") +
  theme_rise() +
  theme_minimal(base_size = 9) +
  facet_grid(Item~Svarskategori,
             switch = "y") +
  theme(legend.position = "top")

Vi kodar om svarskategorierna till siffror inför analysen, och tar bort alla respondenter med missing data. Högre värden = bättre, så gällande fråga 11-12 kodas Ja = 0 och Nej = 1.

Code
df.index <- df %>% 
  filter(Stadium == "Högstadiet") %>% 
  select(q1:q12) %>% 
  mutate(across(q1:q10, ~ car::recode(.x,"'Instämmer inte alls'=0;
                                            2=1;
                                            3=2;
                                            'Instämmer helt'=3;
                                            'Vet ej'=NA;
                                            '<NA>'=NA", 
                                            as.factor = FALSE))) %>% 
  mutate(across(q11:q12, ~ car::recode(.x,"'Nej'=1;
                                       'Ja'=0",
                                       as.factor = FALSE))) %>% 
  na.omit()

4.2 Deskriptiva data

itemnr item
q1 Jag trivs i skolan
q2 Jag trivs i min klass
q3 Jag kan koncentrera mig på lektionerna
q4 Jag kan jobba på lektionerna
q5 Jag lär mig olika saker på lektionerna
q6 Jag känner mig trygg i skolan
q7 Jag blir bra bemött av vuxna i skolan
q8 Jag blir bra bemött av elever i skolan
q9 De vuxna på skolan reagerar om de får reda på att någon har varit elak mot en elev
q10 Jag vet vem på skolan jag kan vända mig till om någon är elak mot mig
q11 Har du varit utsatt för kränkning på din skola i år?
q12 Har du varit utsatt för upprepade kränkningar på din skola i år?
Code
RItileplot(df.index) + theme_minimal() + theme_rise()

Code
RIbarstack(df.index) + theme_minimal() + theme_rise()

Code
RIbarplot(df.index)

Code
RIheatmap(df.index)

4.3 Rasch-analys 1 samtliga items

itemnr item
q1 Jag trivs i skolan
q2 Jag trivs i min klass
q3 Jag kan koncentrera mig på lektionerna
q4 Jag kan jobba på lektionerna
q5 Jag lär mig olika saker på lektionerna
q6 Jag känner mig trygg i skolan
q7 Jag blir bra bemött av vuxna i skolan
q8 Jag blir bra bemött av elever i skolan
q9 De vuxna på skolan reagerar om de får reda på att någon har varit elak mot en elev
q10 Jag vet vem på skolan jag kan vända mig till om någon är elak mot mig
q11 Har du varit utsatt för kränkning på din skola i år?
q12 Har du varit utsatt för upprepade kränkningar på din skola i år?
Code
RIitemfitPCM2(df.index, 250, 32, 8)
OutfitMSQ InfitMSQ OutfitZSTD InfitZSTD
q1 0.794 0.784 -2.186 -2.131
q2 0.848 0.86 -1.291 -1.703
q3 0.989 0.991 -0.092 -0.19
q4 0.905 0.923 -1.03 -0.931
q5 0.997 0.994 0.03 -0.087
q6 0.718 0.747 -2.733 -2.685
q7 1.036 0.971 0.519 -0.398
q8 0.844 0.839 -1.681 -1.678
q9 1.089 1.03 0.785 0.205
q10 1.125 1.082 0.64 0.933
q11 1.08 1.008 0.481 0.167
q12 0.949 0.989 0.126 -0.03
Code
RIloadLoc(df.index)

Code
RIpcmPCA(df.index)
Eigenvalues
2.14
1.77
1.59
1.05
1.02
Code
RIresidcorr(df.index, 0.2)
q1 q2 q3 q4 q5 q6 q7 q8 q9 q10 q11 q12
q1
q2 0.21
q3 -0.15 -0.01
q4 -0.16 -0.12 0.31
q5 -0.2 -0.18 0.02 0.17
q6 0.31 0.08 -0.17 -0.2 -0.21
q7 -0.19 -0.21 -0.21 -0.17 0 -0.17
q8 0.08 0.02 -0.17 -0.17 -0.24 0.19 -0.17
q9 -0.24 -0.26 -0.19 -0.19 -0.06 -0.24 0.12 -0.18
q10 -0.22 -0.23 -0.2 -0.19 -0.11 -0.17 0.06 -0.13 0.12
q11 -0.03 -0.01 -0.12 -0.09 -0.17 0.01 -0.13 0.07 -0.14 -0.18
q12 -0.01 -0.02 -0.1 -0.12 -0.16 0.04 -0.12 0.05 -0.11 -0.17 0.63
Note:
Relative cut-off value (highlighted in red) is 0.126, which is 0.2 above the average correlation.
Code
RItargeting(df.index)

Code
RIitemHierarchy(df.index)

Item fit ser bra ut. PCA eigenvalue och residualkorrelationer visar på att flera frågor är väldigt lika varandra. Tydligast är q11 och 12, vilket är förväntat eftersom de handlar om samma sak med olika svårighetsgrad. H

I övrigt är det främst stora residualkorrelationer mellan:

  • q1 och 2 (trivs i skolan/klassen)
  • q3 och 4 (koncentrera mig/jobba på lektionerna)
  • q1 och 6 (trivs och trygg)
  • q6 och 8 (bra bemött och trygg)

Figuren med faktorladdningar på första kontrastfaktorn visar en möjlig delning i två kluster:

  • 1, 2, 6, 8, 11 och 12 (trivsel, trygghet, frånvaro av kränkningar)
  • 3, 4, 5, 7, 9, 10 (studiero, vuxnas bemötande)

Vi provar att köra två separata analyser.

4.4 Rasch-analys 1 trivsel/trygghet

Code
df.ttf <- df %>% 
  filter(Stadium == "Högstadiet") %>% 
  mutate(across(q1:q10, ~ car::recode(.x,"'Instämmer inte alls'=0;
                                            2=1;
                                            3=2;
                                            'Instämmer helt'=3;
                                            'Vet ej'=NA;
                                            '<NA>'=NA", 
                                            as.factor = FALSE))) %>% 
  mutate(across(q11:q12, ~ car::recode(.x,"'Nej'=0;
                                       'Ja'=1",
                                       as.factor = FALSE))) %>%
  #mutate(q11_12 = q11 + q12) %>% 
  select(q1,q2,q6,q8,q11,q12) %>% 
  na.omit() 

# itemlabels <- rbind(itemlabels,c("q11_12","Har du varit utsatt för (upprepad) kränkning på din skola i år?"))
Code
RItileplot(df.ttf)

itemnr item
q1 Jag trivs i skolan
q2 Jag trivs i min klass
q6 Jag känner mig trygg i skolan
q8 Jag blir bra bemött av elever i skolan
q11 Har du varit utsatt för kränkning på din skola i år?
q12 Har du varit utsatt för upprepade kränkningar på din skola i år?
Code
RIitemfitPCM2(df.ttf, samplesize = 250, nsamples = 32, cpu = 8)
OutfitMSQ InfitMSQ OutfitZSTD InfitZSTD
q1 0.492 0.501 -6.211 -6.424
q2 0.586 0.618 -4.033 -4.346
q6 0.491 0.52 -5.949 -6.153
q8 0.645 0.658 -4.541 -4.407
q11 3.929 1.406 9.46 4.692
q12 4.768 1.107 7.095 1.051
Code
RIpcmPCA(na.omit(df.ttf))
PCA of Rasch model residuals
Eigenvalues
2.29
1.24
1.12
0.87
0.29
Code
RIresidcorr(df.ttf, cutoff = 0.2)
q1 q2 q6 q8 q11 q12
q1
q2 0.13
q6 0.25 -0.01
q8 0.03 -0.05 0.15
q11 -0.44 -0.37 -0.47 -0.46
q12 -0.4 -0.34 -0.44 -0.4 0.72
Note:
Relative cut-off value (highlighted in red) is 0.062, which is 0.2 above the average correlation.
Code
RIloadLoc(df.ttf)

Code
RIitemCats(df.ttf, items = "all", xlims = c(-5,5))

Code
# increase fig-height above as needed, if you have many items
RItargeting(df.ttf)

Code
RIitemHierarchy(df.ttf)

Code
RIpfit(df.ttf)

Code
RItif(df.ttf)

I denna uppsättning items är det tydligt att q11 och q12 tillhör en separat dimension.

Vi tar bort dessa items och ser hur de andra fungerar utan dem.

4.5 Rasch-analys 2 trivsel & trygghet

Code
df.ttf$q11 <- NULL
df.ttf$q12 <- NULL
itemnr item
q1 Jag trivs i skolan
q2 Jag trivs i min klass
q6 Jag känner mig trygg i skolan
q8 Jag blir bra bemött av elever i skolan
Code
RIitemfitPCM2(df.ttf, samplesize = 250, nsamples = 32, cpu = 8)
OutfitMSQ InfitMSQ OutfitZSTD InfitZSTD
q1 0.683 0.674 -3.516 -3.622
q2 0.906 0.877 -0.886 -1.242
q6 0.676 0.668 -3.476 -3.701
q8 0.931 0.923 -0.696 -0.685
Code
RIpcmPCA(na.omit(df.ttf))
PCA of Rasch model residuals
Eigenvalues
1.52
1.39
1.09
0.01
Code
RIresidcorr(df.ttf, cutoff = 0.2)
q1 q2 q6 q8
q1
q2 -0.19
q6 -0.12 -0.39
q8 -0.38 -0.37 -0.19
Note:
Relative cut-off value (highlighted in red) is -0.075, which is 0.2 above the average correlation.
Code
RIloadLoc(df.ttf)

Code
RIitemCats(df.ttf, items = "all", xlims = c(-5,5))

Code
# increase fig-height above as needed, if you have many items
RItargeting(df.ttf)

Code
RIitemHierarchy(df.ttf)

Code
RIpfit(df.ttf)

Code
RItif(df.ttf)

Dessa items fungerar även om flera har låg item fit, men har låg reliabilitet tillsammans. Vi kan eventuellt använda sammansättningen items för att testa invarians/DIF för frågorna (kommer längre ner i denna analys).

4.6 Rasch-analys 1 studiero

Code
df.studiero <- df.index %>% 
  select(!any_of(c("q1","q2","q6","q8","q11","q12")))
Code
RItileplot(df.studiero)

itemnr item
q3 Jag kan koncentrera mig på lektionerna
q4 Jag kan jobba på lektionerna
q5 Jag lär mig olika saker på lektionerna
q7 Jag blir bra bemött av vuxna i skolan
q9 De vuxna på skolan reagerar om de får reda på att någon har varit elak mot en elev
q10 Jag vet vem på skolan jag kan vända mig till om någon är elak mot mig
Code
RIitemfitPCM2(df.studiero, samplesize = 250, nsamples = 32, cpu = 8)
OutfitMSQ InfitMSQ OutfitZSTD InfitZSTD
q3 0.929 0.927 -0.941 -0.709
q4 0.801 0.82 -2.348 -1.953
q5 0.818 0.823 -1.961 -1.988
q7 0.882 0.841 -1.112 -1.602
q9 0.88 0.837 -1.455 -2.115
q10 0.949 0.906 -0.229 -1.23
Code
RIpcmPCA(na.omit(df.studiero))
PCA of Rasch model residuals
Eigenvalues
1.95
1.19
1.04
1.03
0.77
Code
RIresidcorr(df.studiero, cutoff = 0.2)
q3 q4 q5 q7 q9 q10
q3
q4 0.26
q5 -0.08 0.06
q7 -0.31 -0.3 -0.14
q9 -0.32 -0.34 -0.24 -0.02
q10 -0.32 -0.33 -0.28 -0.07 -0.02
Note:
Relative cut-off value (highlighted in red) is 0.036, which is 0.2 above the average correlation.
Code
RIloadLoc(df.studiero)

Code
RIitemCats(df.studiero, items = "all", xlims = c(-5,5))

Code
# increase fig-height above as needed, if you have many items
RItargeting(df.studiero)

Code
RIitemHierarchy(df.studiero)

Code
RIpfit(df.studiero)

Code
RItif(df.studiero)

Item q3 och 4 har en stark residualkorrelation och en av dem behöver tas bort. Vi tar bort q4 eftersom den har något lägre item fit och targeting jämfört med q3.

4.7 Rasch-analys 2 studiero

Code
df.studiero$q4 <- NULL
itemnr item
q3 Jag kan koncentrera mig på lektionerna
q5 Jag lär mig olika saker på lektionerna
q7 Jag blir bra bemött av vuxna i skolan
q9 De vuxna på skolan reagerar om de får reda på att någon har varit elak mot en elev
q10 Jag vet vem på skolan jag kan vända mig till om någon är elak mot mig
Code
RIitemfitPCM2(df.studiero, samplesize = 250, nsamples = 32, cpu = 8)
OutfitMSQ InfitMSQ OutfitZSTD InfitZSTD
q3 1.004 1.008 0.263 0.028
q5 0.828 0.845 -2.092 -1.649
q7 0.766 0.762 -2.268 -2.686
q9 0.75 0.739 -2.92 -3.416
q10 0.796 0.807 -1.503 -1.989
Code
RIpcmPCA(na.omit(df.studiero))
PCA of Rasch model residuals
Eigenvalues
1.60
1.21
1.14
1.04
0.02
Code
RIresidcorr(df.studiero, cutoff = 0.2)
q3 q5 q7 q9 q10
q3
q5 -0.01
q7 -0.29 -0.15
q9 -0.3 -0.26 -0.09
q10 -0.3 -0.29 -0.15 -0.11
Note:
Relative cut-off value (highlighted in red) is 0.005, which is 0.2 above the average correlation.
Code
RIloadLoc(df.studiero)

Code
# increase fig-height above as needed, if you have many items
RItargeting(df.studiero, xlim = c(-4,5))

Code
RIitemHierarchy(df.studiero)

Code
RIpfit(df.studiero)

Code
RItif(df.studiero)

Det är små skillnader mellan T1 och T2 för q10, men de är inte oordnade. I övrigt ser allt acceptabelt ut, dock med en tydlig takeffekt och låg reliabilitet.

Vi återgår till att titta på samtliga items tillsammans och se om det går att ta fram ett fungerande index.

4.8 Rasch-analys 2 samtliga items

Utifrån vår tidigare analys fanns en del residualkorrelationer att hantera. Vi börjar med att ta bort:

  • q4 (q3 har bättre targeting)

Samt att vi slår samman q11 och q12, där svaret “Ja” på någon av frågorna blir “Ja” (0) i den nya frågan, och den som svarat “Nej” på båda frågorna blir “Nej” (1). Vi kodar fortfarande konsekvent så att högre värden = bättre/mera önskvärt resultat.

Det är inte optimalt att ställa två ja/nej-frågor med i princip samma innehåll men olika frekvenspåståenden. Om det i stället hade funnits en enda fråga om utsatthet för kränkning med flera frekvenspåståenden hade det fungerat betydligt bättre. Som det är nu uppstår t.ex. situationen där den som blivit utsatt för flera kränkningar kan svara “Nej” på om den blivit utsatt för en kränkning.

Code
df.index <- df %>% 
  filter(Stadium == "Högstadiet") %>% 
  select(q1:q12) %>% 
  mutate(across(q1:q10, ~ car::recode(.x,"'Instämmer inte alls'=0;
                                            2=1;
                                            3=2;
                                            'Instämmer helt'=3;
                                            'Vet ej'=NA;
                                            '<NA>'=NA", 
                                            as.factor = FALSE))) %>% 
  mutate(across(q11:q12, ~ car::recode(.x,"'Nej'=1;
                                       'Ja'=0",
                                       as.factor = FALSE))) %>% 
  na.omit() %>% 
  mutate(q11q12 = case_when(q11 == 0 | q12 == 0 ~ 0,
                            q11 == 1 & q12 == 1 ~ 1)
         ) %>% 
  select(!any_of(c("q4","q11","q12")))
RItileplot(df.index)

Code
itemlabels <- rbind(itemlabels,c("q11q12","Har du varit utsatt för en eller flera kränkningar på din skola i år?"))
itemnr item
q1 Jag trivs i skolan
q2 Jag trivs i min klass
q3 Jag kan koncentrera mig på lektionerna
q5 Jag lär mig olika saker på lektionerna
q6 Jag känner mig trygg i skolan
q7 Jag blir bra bemött av vuxna i skolan
q8 Jag blir bra bemött av elever i skolan
q9 De vuxna på skolan reagerar om de får reda på att någon har varit elak mot en elev
q10 Jag vet vem på skolan jag kan vända mig till om någon är elak mot mig
q11q12 Har du varit utsatt för en eller flera kränkningar på din skola i år?
Code
RIitemfitPCM2(df.index, 250, 32, 8)
OutfitMSQ InfitMSQ OutfitZSTD InfitZSTD
q1 0.755 0.764 -2.42 -2.653
q2 0.819 0.847 -1.638 -1.485
q3 1.045 1.047 0.587 0.528
q5 1.024 1.023 0.225 0.367
q6 0.684 0.723 -3.086 -3.101
q7 0.97 0.939 -0.234 -0.651
q8 0.816 0.823 -1.916 -1.812
q9 1.023 0.991 0.084 -0.207
q10 1.046 1.03 0.455 0.035
q11q12 1.221 1.039 0.985 0.259
Code
RIloadLoc(df.index)

Code
RIpcmPCA(df.index)
Eigenvalues
2.08
1.42
1.08
1.04
1.01
Code
RIresidcorr(df.index, 0.2)
q1 q2 q3 q5 q6 q7 q8 q9 q10 q11q12
q1
q2 0.2
q3 -0.13 0.01
q5 -0.2 -0.17 0.06
q6 0.3 0.06 -0.15 -0.21
q7 -0.22 -0.24 -0.2 0 -0.21
q8 0.06 0.01 -0.15 -0.23 0.17 -0.2
q9 -0.28 -0.29 -0.18 -0.06 -0.28 0.09 -0.21
q10 -0.26 -0.26 -0.19 -0.11 -0.21 0.03 -0.16 0.09
q11q12 -0.01 0.01 -0.08 -0.14 0.03 -0.12 0.09 -0.13 -0.17
Note:
Relative cut-off value (highlighted in red) is 0.106, which is 0.2 above the average correlation.
Code
RItargeting(df.index)

Code
RIitemHierarchy(df.index)

Som väntat ligger de generella frågorna q1 och q6 lågt i item fit, och korrelerar med andra frågor.

Vi har problematiska residualkorrelationer:

  • q1 med q2 och q6 (trivs i skolan; trivs i klassen; känner mig trygg i skolan )
  • q6 med q8 (och q1), där 8 = blir bra bemött av elever

Vi tar bort q1 först.

Code
df.index %>% 
  select(!q1) %>% 
  RIresidcorr(0.2)
q2 q3 q5 q6 q7 q8 q9 q10 q11q12
q2
q3 0.02
q5 -0.16 0.04
q6 0.12 -0.13 -0.19
q7 -0.23 -0.22 -0.03 -0.19
q8 0.04 -0.14 -0.24 0.21 -0.21
q9 -0.29 -0.21 -0.09 -0.26 0.05 -0.22
q10 -0.26 -0.22 -0.15 -0.2 -0.01 -0.17 0.05
q11q12 0.02 -0.08 -0.15 0.05 -0.12 0.09 -0.14 -0.18
Note:
Relative cut-off value (highlighted in red) is 0.095, which is 0.2 above the average correlation.

6 korrelerar med både 2 och 8, vi tar bort den med.

Code
df.index <- df.index %>% 
  select(!q1) %>%
  select(!q6)

4.9 Rasch-analys 3 samtliga items

itemnr item
q2 Jag trivs i min klass
q3 Jag kan koncentrera mig på lektionerna
q5 Jag lär mig olika saker på lektionerna
q7 Jag blir bra bemött av vuxna i skolan
q8 Jag blir bra bemött av elever i skolan
q9 De vuxna på skolan reagerar om de får reda på att någon har varit elak mot en elev
q10 Jag vet vem på skolan jag kan vända mig till om någon är elak mot mig
q11q12 Har du varit utsatt för en eller flera kränkningar på din skola i år?
Code
RIitemfitPCM2(df.index, 250, 32, 8)
OutfitMSQ InfitMSQ OutfitZSTD InfitZSTD
q2 0.863 0.885 -1.249 -1.231
q3 0.961 0.966 -0.411 -0.45
q5 0.912 0.916 -0.901 -0.922
q7 0.839 0.832 -1.297 -1.871
q8 0.845 0.851 -1.61 -1.885
q9 0.859 0.844 -1.759 -1.892
q10 0.878 0.894 -0.801 -0.997
q11q12 1.139 1.032 0.666 0.486
Code
RIloadLoc(df.index)

Code
RIpcmPCA(df.index)
Eigenvalues
1.70
1.42
1.09
1.01
0.97
Code
RIresidcorr(df.index, 0.2)
q2 q3 q5 q7 q8 q9 q10 q11q12
q2
q3 0.03
q5 -0.16 0.03
q7 -0.22 -0.24 -0.05
q8 0.09 -0.12 -0.22 -0.19
q9 -0.29 -0.24 -0.13 0.02 -0.21
q10 -0.25 -0.25 -0.18 -0.04 -0.15 0.01
q11q12 0.04 -0.08 -0.15 -0.12 0.11 -0.14 -0.18
Note:
Relative cut-off value (highlighted in red) is 0.082, which is 0.2 above the average correlation.
Code
RItargeting(df.index)

Code
RIitemHierarchy(df.index)

Code
RItif(df.index)

Code
RIitemCats(df.index)

Code
RIpfit(df.index)

Två korrelationer strax över gränsvärdet, vilket inte är problematiskt.

Code
RIlistitems(df.index)
itemnr item
q2 Jag trivs i min klass
q3 Jag kan koncentrera mig på lektionerna
q5 Jag lär mig olika saker på lektionerna
q7 Jag blir bra bemött av vuxna i skolan
q8 Jag blir bra bemött av elever i skolan
q9 De vuxna på skolan reagerar om de får reda på att någon har varit elak mot en elev
q10 Jag vet vem på skolan jag kan vända mig till om någon är elak mot mig
q11q12 Har du varit utsatt för en eller flera kränkningar på din skola i år?

4.10 DIF TTS-index

Vi behöver undersöka huruvida frågorna fungerar olika för subgrupper, i detta fall kön och årskurs/stadium.

Code
studiero.items <- names(df.studiero)
ttf.items <- names(df.ttf)
tts.items <- names(df.index)

Vi börjar med enbart högstadiet. “Annat/Vill ej svara” är en för liten grupp, så den kodas som missing.

Funktionen som gör DIF-analysen fungerar inte korrekt när item q11 är med, eftersom det bara har två svarskategorier. Vi gör därför LRT-analyser som inkluderar q11, och DIF-analyser utan q11. LRT står för Likelihood Ratio Test. Eftersom vi har väldigt stora sampel är det hög sannolikhet att få statistisk signifikans i dessa test, även om skillnaderna är såpass små att de inte är relevanta. Därför behöver vi även undersöka storleken av effekterna på item-nivå.

Code
df.dif.tts <- df %>% 
  filter(Stadium == "Högstadiet") %>% 
  mutate(Kön = car::recode(Kön,"'Annat/Vill ej svara'=NA")) %>% 
  #drop_na(Stadium, Kön) %>% 
  mutate(across(q1:q10, ~ car::recode(.x,"'Instämmer inte alls'=0;
                                            2=1;
                                            3=2;
                                            'Instämmer helt'=3;
                                            'Vet ej'=NA;
                                            '<NA>'=NA", 
                                            as.factor = FALSE))) %>% 
  mutate(across(q11:q12, ~ car::recode(.x,"'Nej'=1;
                                       'Ja'=0",
                                       as.factor = FALSE))) %>% 
  mutate(q11q12 = case_when(q11 == 0 | q12 == 0 ~ 0,
                            q11 == 1 & q12 == 1 ~ 1)
         ) %>% 
  select(all_of(tts.items),Kön,Årskurs,Stadium) %>% 
  na.omit()  
  
dif.tts.kön <- df.dif.tts$Kön
dif.tts.årskurs <- df.dif.tts$Årskurs
dif.tts.stadium <- factor(df.dif.tts$Stadium,
                          levels = c("Lågstadiet","Mellanstadiet","Högstadiet"))

df.dif.tts$Kön <- NULL
df.dif.tts$Årskurs <- NULL
df.dif.tts$Stadium <- NULL
itemnr item
q2 Jag trivs i min klass
q3 Jag kan koncentrera mig på lektionerna
q5 Jag lär mig olika saker på lektionerna
q7 Jag blir bra bemött av vuxna i skolan
q8 Jag blir bra bemött av elever i skolan
q9 De vuxna på skolan reagerar om de får reda på att någon har varit elak mot en elev
q10 Jag vet vem på skolan jag kan vända mig till om någon är elak mot mig
q11q12 Har du varit utsatt för en eller flera kränkningar på din skola i år?

4.10.1 Kön

Code
RIdifTableLR(df.dif.tts, dif.tts.kön)
Item locations
Standard errors
Item Flicka Pojke MaxDiff All SE_Flicka SE_Pojke SE_All
q2 -0.71 -0.504 0.206 -0.631 0.122 0.131 0.089
q3 1.044 0.825 0.219 0.942 0.097 0.104 0.071
q5 -0.457 -0.546 0.089 -0.492 0.120 0.134 0.089
q7 -0.743 -0.243 0.5 -0.499 0.124 0.119 0.086
q8 -0.285 -0.747 0.462 -0.507 0.115 0.140 0.088
q9 0.954 0.876 0.078 0.920 0.090 0.095 0.065
q10 0.199 0.477 0.278 0.332 0.099 0.103 0.072
q11q12 -0.007 -0.417 0.41 -0.194 0.058 0.065 0.043
Note:
Values highlighted in red are above the chosen cutoff 0.5 logits. Background color brown and blue indicate the lowest and highest values among the DIF groups.
Code
RIdifFigureLR(df.dif.tts, dif.tts.kön)

Code
RIdifThreshFigLR(df.dif.tts, dif.tts.kön)

Code
df.dif.tts %>% 
  select(!q11q12) %>% 
  RIdifTable(., dif.tts.kön)

Item 2 3 Mean location StDev MaxDiff
q2 -0.425 -0.421 -0.423 0.003 0.004
q3 0.749 0.684 0.716 0.046 0.065
q5 -0.091 -0.085 -0.088 0.004 0.006
q7 -0.398 -0.216 -0.307 0.129 0.182
q8 -0.021 -0.298 -0.160 0.196 0.277
q9 0.385 0.379 0.382 0.004 0.006
q10 -0.198 -0.043 -0.121 0.110 0.155
Code
df.dif.tts %>% 
  select(!q11q12) %>% 
  RIdifFigThresh(., dif.tts.kön)

Väldigt olika utfall mellan de två DIF-metoderna. Men eftersom ingen av dem indikerar DIF för q11q12 väljer vi att gå på analys-metoden som plockar bort q11q12.

itemnr item
q2 Jag trivs i min klass
q3 Jag kan koncentrera mig på lektionerna
q5 Jag lär mig olika saker på lektionerna
q7 Jag blir bra bemött av vuxna i skolan
q8 Jag blir bra bemött av elever i skolan
q9 De vuxna på skolan reagerar om de får reda på att någon har varit elak mot en elev
q10 Jag vet vem på skolan jag kan vända mig till om någon är elak mot mig
q11q12 Har du varit utsatt för en eller flera kränkningar på din skola i år?
Code
RIdifTableLR(df.dif.tts, dif.tts.årskurs)
Item locations
Standard errors
Item Åk 7 Åk 8 Åk 9 MaxDiff All SE_Åk 7 SE_Åk 8 SE_Åk 9 SE_All
q2 -0.451 -0.687 -0.74 0.289 -0.631 0.156 0.166 0.145 0.089
q3 1.048 0.936 0.854 0.194 0.942 0.125 0.129 0.115 0.071
q5 -0.623 -0.297 -0.513 0.326 -0.492 0.168 0.155 0.145 0.089
q7 -0.537 -0.402 -0.534 0.135 -0.499 0.159 0.152 0.138 0.086
q8 -0.212 -0.709 -0.607 0.497 -0.507 0.149 0.169 0.144 0.088
q9 0.497 0.959 1.226 0.729 0.920 0.124 0.118 0.104 0.065
q10 0.325 0.232 0.423 0.191 0.332 0.130 0.133 0.113 0.072
q11q12 -0.14 -0.095 -0.327 0.232 -0.194 0.074 0.077 0.074 0.043
Note:
Values highlighted in red are above the chosen cutoff 0.5 logits. Background color brown and blue indicate the lowest and highest values among the DIF groups.
4.10.1.1 utan q11q12 (annan metod)
Code
df.dif.tts %>% 
  select(!q11q12) %>% 
  RIdifTable(., dif.tts.årskurs)

Item 2 4 5 Mean location StDev MaxDiff
q2 -0.310 -0.450 -0.530 -0.430 0.111 0.220
q3 0.801 0.719 0.633 0.718 0.084 0.168
q5 -0.180 0.009 -0.057 -0.076 0.096 0.189
q7 -0.335 -0.262 -0.318 -0.305 0.039 0.074
q8 -0.021 -0.253 -0.212 -0.162 0.124 0.233
q9 0.185 0.422 0.529 0.379 0.176 0.344
q10 -0.140 -0.186 -0.044 -0.123 0.072 0.141

4.10.2 Interaktion kön/årskurs

itemnr item
q2 Jag trivs i min klass
q3 Jag kan koncentrera mig på lektionerna
q5 Jag lär mig olika saker på lektionerna
q7 Jag blir bra bemött av vuxna i skolan
q8 Jag blir bra bemött av elever i skolan
q9 De vuxna på skolan reagerar om de får reda på att någon har varit elak mot en elev
q10 Jag vet vem på skolan jag kan vända mig till om någon är elak mot mig
q11q12 Har du varit utsatt för en eller flera kränkningar på din skola i år?
Code
df.dif.tts %>% 
  select(!q11q12) %>% 
  RIdifTable2(., dif.tts.kön, dif.tts.årskurs)

Item 3 4 7 8 9 Mean location StDev MaxDiff
q2 -0.324 -0.287 -0.416 -0.532 -0.492 -0.410 0.105 0.245
q3 0.878 0.731 0.773 0.607 0.663 0.730 0.104 0.271
q5 -0.109 -0.270 -0.056 -0.076 0.012 -0.100 0.105 0.282
q7 -0.440 -0.240 -0.281 -0.457 -0.198 -0.323 0.118 0.259
q8 0.081 -0.101 -0.136 -0.024 -0.418 -0.120 0.186 0.499
q9 0.181 0.191 0.392 0.568 0.477 0.362 0.172 0.386
q10 -0.267 -0.025 -0.276 -0.086 -0.045 -0.140 0.122 0.251

Item 8 får något högre maxvärde här, men fortfarande under 0.5.

4.10.3 Stadium

Vi tar inte med förskoleklass, som har lite data.

Code
df.dif.tts <- df %>% 
  filter(!Stadium == "Förskoleklass") %>% 
  mutate(Kön = car::recode(Kön,"'Annat/Vill ej svara'=NA")) %>% 
  drop_na(Stadium, Kön) %>% 
  mutate(across(q1:q10, ~ car::recode(.x,"'Instämmer inte alls'=0;
                                            2=1;
                                            3=2;
                                            'Instämmer helt'=3;
                                            'Vet ej'=NA;
                                            '<NA>'=NA", 
                                            as.factor = FALSE))) %>% 
  mutate(across(q11:q12, ~ car::recode(.x,"'Nej'=1;
                                       'Ja'=0",
                                       as.factor = FALSE))) %>% 
  mutate(q11q12 = case_when(q11 == 0 | q12 == 0 ~ 0,
                            q11 == 1 & q12 == 1 ~ 1)
         ) %>% 
  select(all_of(tts.items),Kön,Årskurs,Stadium,År) %>% 
  na.omit()  
  
dif.tts.kön <- df.dif.tts$Kön
dif.tts.årskurs <- df.dif.tts$Årskurs
dif.tts.stadium <- factor(df.dif.tts$Stadium,
                          levels = c("Lågstadiet","Mellanstadiet","Högstadiet"))
dif.tts.år <- df.dif.tts$År

df.dif.tts$Kön <- NULL
df.dif.tts$Årskurs <- NULL
df.dif.tts$Stadium <- NULL
df.dif.tts$År <- NULL
itemnr item
q2 Jag trivs i min klass
q3 Jag kan koncentrera mig på lektionerna
q5 Jag lär mig olika saker på lektionerna
q7 Jag blir bra bemött av vuxna i skolan
q8 Jag blir bra bemött av elever i skolan
q9 De vuxna på skolan reagerar om de får reda på att någon har varit elak mot en elev
q10 Jag vet vem på skolan jag kan vända mig till om någon är elak mot mig
q11q12 Har du varit utsatt för en eller flera kränkningar på din skola i år?
Code
RIdifTableLR(df.dif.tts, dif.tts.stadium)
Item locations
Standard errors
Item Lågstadiet Mellanstadiet Högstadiet MaxDiff All SE_Lågstadiet SE_Mellanstadiet SE_Högstadiet SE_All
q2 -0.331 -0.647 -0.631 0.316 -0.521 0.089 0.096 0.089 0.052
q3 0.907 0.91 0.942 0.035 0.906 0.065 0.070 0.071 0.039
q5 -0.611 -1.105 -0.492 0.613 -0.784 0.101 0.112 0.089 0.056
q7 -0.554 -0.349 -0.499 0.205 -0.484 0.101 0.088 0.086 0.052
q8 0.061 -0.075 -0.507 0.568 -0.134 0.078 0.083 0.088 0.047
q9 -0.049 0.788 0.92 0.969 0.565 0.083 0.067 0.065 0.040
q10 0.286 0.281 0.332 0.051 0.272 0.087 0.076 0.072 0.044
q11q12 0.876 0.593 -0.194 1.07 0.541 0.035 0.035 0.043 0.021
Note:
Values highlighted in red are above the chosen cutoff 0.5 logits. Background color brown and blue indicate the lowest and highest values among the DIF groups.
Code
df.dif.tts %>% 
  select(!q11q12) %>% 
  RIdifTable(., dif.tts.stadium) 

Item 2 4 5 Mean location StDev MaxDiff
q2 -0.126 -0.302 -0.429 -0.286 0.152 0.303
q3 0.687 0.740 0.719 0.715 0.027 0.053
q5 -0.329 -0.495 -0.084 -0.303 0.207 0.411
q7 -0.325 -0.265 -0.309 -0.300 0.031 0.061
q8 0.277 0.121 -0.159 0.080 0.221 0.436
q9 -0.029 0.358 0.383 0.237 0.231 0.412
q10 -0.154 -0.157 -0.121 -0.144 0.020 0.036

Här finns tecken på att q11q12 fungerar olika mellan stadier.

4.10.4 DIF kön (samtliga åk 1-9)

Code
df.dif.tts %>% 
  select(!q11q12) %>% 
  RIdifTable(., dif.tts.kön) 

Item 2 3 Mean location StDev MaxDiff
q2 -0.237 -0.305 -0.271 0.048 0.068
q3 0.787 0.647 0.717 0.099 0.140
q5 -0.399 -0.274 -0.337 0.088 0.125
q7 -0.397 -0.227 -0.312 0.120 0.170
q8 0.213 0.015 0.114 0.140 0.198
q9 0.239 0.246 0.242 0.005 0.007
q10 -0.206 -0.102 -0.154 0.074 0.105
4.10.4.1 Årtal
Code
df.dif.tts %>%
  select(!q11q12) %>% 
  RIdifTable(., dif.tts.år) 

Item 4 5 6 8 9 Mean location StDev MaxDiff
q2 -0.289 -0.385 -0.231 -0.257 -0.216 -0.275 0.067 0.169
q3 0.918 0.804 0.781 0.503 0.501 0.701 0.189 0.417
q5 -0.446 -0.361 -0.376 -0.241 -0.227 -0.330 0.094 0.219
q7 -0.292 -0.240 -0.351 -0.355 -0.289 -0.305 0.048 0.114
q8 0.003 0.109 0.107 0.155 0.179 0.110 0.068 0.176
q9 0.241 0.278 0.236 0.271 0.196 0.244 0.033 0.083
q10 -0.135 -0.204 -0.166 -0.076 -0.145 -0.145 0.047 0.128

:::

4.10.4.2 DIF item fit
Code
df.dif.tts <- df %>% 
  filter(!Stadium == "Förskoleklass") %>% 
  mutate(Kön = car::recode(Kön,"'Annat/Vill ej svara'=NA")) %>% 
  drop_na(Stadium,Kön) %>% 
  mutate(across(q1:q10, ~ car::recode(.x,"'Instämmer inte alls'=0;
                                            2=1;
                                            3=2;
                                            'Instämmer helt'=3;
                                            'Vet ej'=NA;
                                            '<NA>'=NA", 
                                            as.factor = FALSE))) %>% 
  mutate(across(q11:q12, ~ car::recode(.x,"'Nej'=1;
                                       'Ja'=0",
                                       as.factor = FALSE))) %>% 
  mutate(q11q12 = case_when(q11 == 0 | q12 == 0 ~ 0,
                            q11 == 1 & q12 == 1 ~ 1)
         ) %>% 
  select(all_of(tts.items),Kön,Årskurs,Stadium) %>% 
  mutate(Stadium = as.character(Stadium)) %>% # split() i nästa chunk funkar inte med en faktor-variabel om inte alla faktornivåer finns med. Det hade iofs gått bra att köra droplevel() också
  na.omit()  
  
dif.tts.kön <- df.dif.tts$Kön
dif.tts.årskurs <- df.dif.tts$Årskurs
dif.tts.stadium <- df.dif.tts$Stadium

df.dif.tts$Kön <- NULL
df.dif.tts$Årskurs <- NULL
Code
library(furrr)
plan(multisession, workers = 8)

itemfit.time <- df.dif.tts %>% 
  split(.$Stadium) %>% # split() funkar inte med en faktor-variabel om inte alla faktornivåer finns med, behöver kodas om till chr
  future_map(~ RIitemfitPCM(subset(., select = -Stadium), table = FALSE), 
             .options = furrr_options(seed = TRUE))

stadier <- c("Lågstadiet","Mellanstadiet","Högstadiet")
itemfit.all <- data.frame(matrix(ncol = 4, nrow = 0))
colnames(itemfit.all) <- c("Item", "Stadium", 'InfitMSQ','OutfitMSQ')

for (i in stadier) {
  itemfit.add <- itemfit.time[[i]] %>% 
    rownames_to_column("Item") %>% 
    add_column(Stadium = i)
  
  itemfit.all <- rbind(itemfit.all, itemfit.add)
}
Code
itemfit.all %>%
  group_by(Item) %>%
  summarise(
    mean_Out = mean(OutfitMSQ),
    sd_Out = sd(OutfitMSQ),
    max_Out = max(OutfitMSQ),
    min_Out = min(OutfitMSQ),
    mean_In = mean(InfitMSQ),
    sd_In = sd(InfitMSQ),
    max_In = max(InfitMSQ),
    min_In = min(InfitMSQ)
  ) %>%
  mutate(
    diffmax_OutfitMSQ = max_Out - min_Out,
    diffmax_InfitMSQ = max_In - min_In,
    item = Item
  ) %>%
  mutate(across(where(is.numeric), ~ round(.x, 2))) %>%
  mutate(across(starts_with(c("max", "min")), ~ cell_spec(.x, color = ifelse(.x < 0.7, "red",
    ifelse(.x > 1.3, "red", "black")
  )))) %>%
  kbl(booktabs = T, escape = F,
      col.names = c("Item","Mean","SD","Max","Min","Mean","SD","Max","Min","OutfitMSQ","InfitMSQ","Item")) %>%
  # bootstrap options are for HTML output
  kable_styling(
    bootstrap_options = c("striped", "hover"),
    position = "left",
    full_width = F,
    font_size = 15,
    fixed_thead = T
  ) %>%
  column_spec(c(1, 12), bold = T) %>%
  kable_classic(html_font = "Lato") %>%
  # latex_options are for PDF output
  kable_styling(latex_options = c("striped", "scale_down")) %>%
  add_header_above(c(" " = 1, 
                     "OutfitMSQ" = 4, 
                     "InfitMSQ" = 4, 
                     "Max differences" = 2,
                     " " = 1))
OutfitMSQ
InfitMSQ
Max differences
Item Mean SD Max Min Mean SD Max Min OutfitMSQ InfitMSQ Item
q10 0.83 0.09 0.88 0.73 0.88 0.03 0.9 0.85 0.15 0.05 q10
q11q12 1.20 0.09 1.3 1.14 1.07 0.07 1.15 1.03 0.16 0.12 q11q12
q2 0.80 0.07 0.86 0.72 0.84 0.05 0.89 0.8 0.14 0.09 q2
q3 0.98 0.03 1.01 0.96 0.99 0.02 1.01 0.97 0.05 0.05 q3
q5 0.87 0.05 0.91 0.82 0.90 0.02 0.92 0.88 0.09 0.03 q5
q7 0.75 0.09 0.84 0.65 0.80 0.04 0.83 0.76 0.18 0.08 q7
q8 0.85 0.01 0.87 0.85 0.86 0.02 0.89 0.85 0.02 0.03 q8
q9 0.84 0.02 0.86 0.82 0.85 0.01 0.85 0.84 0.03 0.01 q9

5 Rasch-analys 4

Först tas q11q12 bort.

Code
df.index <- df.dif.tts %>% 
  select(!q11q12) %>% 
  select(!Stadium)

Vi lägger nu in alla responser från alla år och årskurser

Code
RIitemfitPCM2(df.index, 250, 32, 8)
OutfitMSQ InfitMSQ OutfitZSTD InfitZSTD
q2 0.858 0.88 -1.356 -1.296
q3 1.016 1.016 0.03 0.173
q5 0.857 0.879 -1.159 -1.157
q7 0.746 0.785 -1.805 -1.969
q8 0.963 0.947 -0.636 -0.688
q9 0.832 0.826 -1.072 -1.567
q10 0.822 0.856 -0.902 -1.358
Code
RIloadLoc(df.index)

Code
RIpcmPCA(df.index)
Eigenvalues
1.53
1.31
1.11
1.06
0.99
Code
RIresidcorr(df.index, 0.2)
q2 q3 q5 q7 q8 q9 q10
q2
q3 -0.03
q5 -0.16 -0.07
q7 -0.19 -0.23 -0.06
q8 0.05 -0.16 -0.23 -0.15
q9 -0.26 -0.25 -0.11 0.04 -0.22
q10 -0.2 -0.23 -0.13 -0.08 -0.16 -0.04
Note:
Relative cut-off value (highlighted in red) is 0.063, which is 0.2 above the average correlation.
Code
RItargeting(df.index)

Code
RIitemHierarchy(df.index)

Code
RIitemCats(df.index)

Item q10 har oordnade svarskategorier, de två lägsta och de två högsta slås ihop.

Code
df.index$q10 <- car::recode(df.index$q10,"1=0;2=1;3=1")

5.1 Targeting

Code
RItargeting(df.index)

5.2 Reliabilitet

Code
RItif(df.index)

5.3 Person fit

Code
RIpfit(df.index)

Targeting är mycket skev, det skiljer 1.8 logits mellan items och respondenter. Detta framgår också av reliabilitetskurvan, där endast ca 30% av respondenterna befinner sig i området med acceptabel reliabilitet.

5.4 Item-parametrar

Code
RIitemparams(df.index)
Threshold 1 Threshold 2 Threshold 3 Item location
q2 -0.80 -0.13 1.29 0.12
q3 -0.47 0.83 3.04 1.13
q5 -1.16 -0.06 1.40 0.06
q7 -0.71 -0.12 1.10 0.09
q8 -0.92 0.24 2.22 0.51
q9 -0.21 0.55 1.62 0.66
q10 -0.51 NA NA -0.51

5.5 Transformeringstabell

Förutsätter att item 10 har dikotomiserats genom att slå samman de två lägsta och de två högsta svarsalternativen. Det ger oss 6 frågor med 3 trösklar vardera och en fråga med en tröskel, alltså 6*3+1 = 19 som max på ordinal summapoäng.

Code
#RIscoreSE(df.index)

6 DIF för trygghet/trivsel

Eftersom dessa enstaka frågor ofta särredovisas är det viktigt att undersöka jämförbarhet mellan kön och ålder.

Code
df.dif.tts <- df %>% 
  filter(!Stadium == "Förskoleklass") %>% 
  mutate(Kön = car::recode(Kön,"'Annat/Vill ej svara'=NA")) %>% 
  drop_na(Stadium, Kön) %>% 
  mutate(across(q1:q10, ~ car::recode(.x,"'Instämmer inte alls'=0;
                                            2=1;
                                            3=2;
                                            'Instämmer helt'=3;
                                            'Vet ej'=NA;
                                            '<NA>'=NA", 
                                            as.factor = FALSE))) %>% 
  mutate(across(q11:q12, ~ car::recode(.x,"'Nej'=1;
                                       'Ja'=0",
                                       as.factor = FALSE))) %>% 
  mutate(q11q12 = case_when(q11 == 0 | q12 == 0 ~ 0,
                            q11 == 1 & q12 == 1 ~ 1)
         ) %>% 
  select(q1:q10,Kön,Årskurs,Stadium,År) %>% 
  select(!q4) %>% 
  na.omit()  
  
dif.tts.kön <- df.dif.tts$Kön
dif.tts.årskurs <- df.dif.tts$Årskurs
dif.tts.stadium <- factor(df.dif.tts$Stadium,
                          levels = c("Lågstadiet","Mellanstadiet","Högstadiet"))
dif.tts.år <- df.dif.tts$År

df.dif.tts$Kön <- NULL
df.dif.tts$Årskurs <- NULL
df.dif.tts$Stadium <- NULL
df.dif.tts$År <- NULL

Vi behöver även undersöka stabiliteten i item fit, åtminstone fördelat på stadium. För detta ändamål återinför vi items 1 och 6.

Code
RIdifTable(df.dif.tts, dif.tts.kön)

Item 2 3 Mean location StDev MaxDiff
q1 -0.140 -0.170 -0.155 0.022 0.031
q2 -0.215 -0.270 -0.243 0.039 0.055
q3 0.864 0.739 0.802 0.089 0.125
q5 -0.368 -0.228 -0.298 0.099 0.140
q6 -0.106 -0.206 -0.156 0.070 0.100
q7 -0.382 -0.190 -0.286 0.136 0.192
q8 0.258 0.069 0.163 0.134 0.189
q9 0.283 0.317 0.300 0.024 0.034
q10 -0.193 -0.060 -0.126 0.094 0.133
Code
RIdifTable(df.dif.tts, dif.tts.stadium)

Item 2 4 5 Mean location StDev MaxDiff
q1 -0.026 -0.144 -0.335 -0.168 0.156 0.309
q2 -0.131 -0.272 -0.365 -0.256 0.118 0.234
q3 0.738 0.833 0.855 0.809 0.062 0.117
q5 -0.339 -0.464 0.015 -0.263 0.248 0.479
q6 0.009 -0.192 -0.322 -0.168 0.167 0.332
q7 -0.351 -0.237 -0.231 -0.273 0.068 0.120
q8 0.294 0.180 -0.071 0.134 0.186 0.364
q9 -0.024 0.431 0.493 0.300 0.282 0.517
q10 -0.171 -0.135 -0.041 -0.116 0.067 0.130
Code
RIdifTable(df.dif.tts, dif.tts.årskurs)

Item 3 5 6 9 11 12 15 16 17 Mean location StDev MaxDiff
q1 -0.001 -0.030 -0.035 -0.047 -0.135 -0.219 -0.320 -0.283 -0.398 -0.163 0.147 0.397
q2 -0.086 -0.055 -0.274 -0.220 -0.305 -0.283 -0.254 -0.410 -0.438 -0.258 0.128 0.383
q3 0.767 0.726 0.725 0.850 0.886 0.783 0.916 0.850 0.800 0.811 0.068 0.190
q5 -0.193 -0.329 -0.539 -0.425 -0.434 -0.509 -0.107 0.104 0.076 -0.262 0.244 0.643
q6 0.016 0.030 0.003 -0.115 -0.242 -0.209 -0.192 -0.275 -0.493 -0.164 0.169 0.523
q7 -0.443 -0.397 -0.206 -0.391 -0.174 -0.172 -0.279 -0.194 -0.210 -0.274 0.108 0.271
q8 0.312 0.293 0.282 0.286 0.124 0.128 0.047 -0.191 -0.083 0.133 0.181 0.502
q9 -0.082 -0.042 0.078 0.223 0.510 0.524 0.268 0.529 0.673 0.298 0.275 0.756
q10 -0.290 -0.195 -0.035 -0.161 -0.229 -0.043 -0.078 -0.129 0.072 -0.121 0.112 0.362
Code
RIdifTable(df.dif.tts, dif.tts.år)

Item 4 5 6 8 9 Mean location StDev MaxDiff
q1 -0.191 -0.181 -0.157 -0.135 -0.114 -0.156 0.032 0.077
q2 -0.246 -0.336 -0.215 -0.218 -0.212 -0.245 0.052 0.123
q3 1.017 0.882 0.880 0.574 0.577 0.786 0.200 0.444
q5 -0.407 -0.309 -0.346 -0.201 -0.197 -0.292 0.092 0.210
q6 -0.211 -0.143 -0.170 -0.198 -0.085 -0.161 0.050 0.126
q7 -0.239 -0.230 -0.310 -0.315 -0.294 -0.277 0.040 0.085
q8 0.051 0.166 0.152 0.211 0.217 0.159 0.067 0.166
q9 0.314 0.329 0.302 0.331 0.239 0.303 0.038 0.092
q10 -0.089 -0.178 -0.136 -0.049 -0.131 -0.117 0.049 0.129
Code
RIdifTable2(df.dif.tts, dif.tts.stadium, dif.tts.kön)

Item 3 4 7 8 10 11 Mean location StDev MaxDiff
q1 -0.007 -0.040 -0.076 -0.194 -0.345 -0.313 -0.163 0.144 0.339
q2 -0.133 -0.129 -0.155 -0.376 -0.367 -0.351 -0.252 0.124 0.247
q3 0.818 0.673 0.890 0.796 0.889 0.812 0.813 0.079 0.217
q5 -0.455 -0.251 -0.621 -0.344 0.006 0.012 -0.275 0.253 0.633
q6 0.068 -0.039 -0.096 -0.264 -0.294 -0.340 -0.161 0.162 0.408
q7 -0.399 -0.310 -0.380 -0.126 -0.331 -0.131 -0.279 0.121 0.272
q8 0.337 0.259 0.309 0.077 0.078 -0.222 0.139 0.210 0.558
q9 -0.031 -0.015 0.370 0.483 0.489 0.492 0.298 0.253 0.523
q10 -0.198 -0.148 -0.241 -0.051 -0.124 0.041 -0.120 0.102 0.281

7 Visualiseringar

Code
df <- df %>%
  mutate(diskriminerad = case_when(q16 == "Chosen" ~ "Kön",
                            q17 == "Chosen" ~ "Etnisk bakgrund",
                            q18 == "Chosen" ~ "Religion eller trosuppfattning",
                            q19 == "Chosen" ~ "Funktionshinder",
                            q20 == "Chosen" ~ "Sexuell läggning",
                            q21 == "Chosen" ~ "Ålder",
                            q22 == "Chosen" ~ "Könsidentitet/ könsuttryck",
                            q23 == "Chosen" ~ "Annat",
                            TRUE ~ "Ej diskriminerad"
  )) %>%
  mutate(diskriminerad = factor(diskriminerad))
#df %>% count(diskriminerad)
Code
df.viz <- df %>% 
  filter(!Stadium == "Förskoleklass") %>% 
  mutate(Kön = car::recode(Kön,"'Annat/Vill ej svara'=NA")) %>% 
  drop_na(Stadium,Kön,Årskurs,År) %>% 
  # mutate(across(q1:q10, ~ car::recode(.x,"'Instämmer inte alls'=0;
  #                                           2=1;
  #                                           3=2;
  #                                           'Instämmer helt'=3;
  #                                           'Vet ej'=NA;
  #                                           '<NA>'=NA", 
  #                                           as.factor = FALSE))) %>% 
  # mutate(across(q11:q12, ~ car::recode(.x,"'Nej'=1;
  #                                      'Ja'=0",
  #                                      as.factor = FALSE))) %>% 
  mutate(q11q12 = case_when(q11 == 'Nej' & q12 == 'Nej' ~ 'Nej',
                      q11 == 'Ja' | q12 == 'Ja' ~ 'Ja',
                      TRUE ~ NA)
  ) %>%
  select(q1:q34,q11q12,diskriminerad,Kön,Årskurs,Stadium,År,Skola) %>% 
  mutaterskurs = droplevels(Årskurs),
         Stadium = droplevels(Stadium)) %>% 
  mutate(Stadium = factor(Stadium, levels = 
                            c("Lågstadiet","Mellanstadiet","Högstadiet")))

7.1 q11 och q12

Code
df.viz %>% 
  select(q11,q12,Årskurs,Kön) %>%
  na.omit() %>% 
  pivot_longer(q11:q12,
               names_to = "item",
               values_to = "svarskategori") %>% 
  ggplot(aes(x = svarskategori, fill = Kön)) +
  geom_bar(position = "dodge") +
  facet_grid(item~Årskurs) +
  scale_fill_gender() +
  theme_rise() +
  theme_minimal() +
  labs(title = "Utsatt för kränkningar - antal",
       y = "Antal",
       x = "")

Code
df.viz %>% 
  select(q11,q12,Årskurs,Kön) %>%
  na.omit() %>% 
  pivot_longer(q11:q12,
               names_to = "item",
               values_to = "svarskategori") %>% 
  group_by(Årskurs,Kön,item) %>% 
  count(svarskategori) %>% 
  mutate(Andel = n * 100/sum(n)) %>% 
  ungroup() %>% 
  ggplot(aes(x = svarskategori, y = Andel, fill = Kön)) +
  geom_col(position = "dodge") +
  facet_grid(item~Årskurs) +
  scale_fill_gender() +
  theme_rise() +
  theme_minimal() +
  labs(title = "Utsatt för kränkningar - andel i %",
       y = "Andel",
       x = "") +
  scale_y_continuous(limits = c(0,100))

Code
df.viz %>% 
  select(q11,q12,Årskurs,Kön) %>%
  pivot_longer(c("q11","q12"),
               names_to = "item",
               values_to = "svarskategori") %>% 
  group_by(Årskurs,Kön,item) %>% 
  count(svarskategori) %>% 
  mutate(Andel = n * 100/sum(n)) %>% 
  ungroup() %>% 
  filter(svarskategori == "Ja") %>% 
  ggplot(aes(x = Årskurs, y = Andel, color = Kön, group = Kön)) +
  geom_point(size = 2.5) +
  geom_line(linewidth = 1.1) +
  facet_wrap(~item) +
  scale_color_gender() +
  theme_rise() +
  theme_minimal() +
  labs(title = "Utsatt för kränkningar - andel i %",
       y = "Andel",
       x = "") +
  scale_y_continuous(limits = c(0,100))

Code
df.viz %>% 
  select(q11,q12,Årskurs,Kön) %>%
  pivot_longer(c("q11","q12"),
               names_to = "item",
               values_to = "svarskategori") %>% 
  group_by(Årskurs,Kön,item) %>% 
  count(svarskategori) %>% 
  mutate(Andel = n * 100/sum(n)) %>% 
  ungroup() %>% 
  filter(svarskategori == "Nej") %>% 
  ggplot(aes(x = Årskurs, y = Andel, color = Kön, group = Kön)) +
  geom_point(size = 2.5) +
  geom_line(linewidth = 1.1) +
  facet_wrap(~item) +
  scale_color_gender() +
  theme_rise() +
  theme_minimal() +
  labs(title = "Ej utsatt för kränkningar - andel i %",
       y = "Andel",
       x = "") +
  scale_y_continuous(limits = c(0,100))

Code
df.viz %>% 
  select(q11,q12,Årskurs,Kön,År) %>%
  pivot_longer(c("q11","q12"),
               names_to = "item",
               values_to = "svarskategori") %>% 
  group_by(År,Årskurs,Kön,item) %>% 
  count(svarskategori) %>% 
  mutate(Andel = n * 100/sum(n)) %>% 
  ungroup() %>% 
  filter(svarskategori == "Ja") %>% 
  ggplot(aes(x = År, y = Andel, color = Kön, group = Kön)) +
  geom_point(size = 2) +
  geom_line(linewidth = 1) +
  facet_grid(item~Årskurs) +
  scale_color_gender() +
  theme_rise() +
  theme_minimal() +
  labs(title = "Utsatt för kränkningar - andel i %",
       y = "Andel",
       x = "") +
  scale_y_continuous(limits = c(0,100), 
                     minor_breaks = NULL, 
                     breaks = c(0,20,40,60,80,100)) +
  scale_x_continuous(guide = guide_axis(n.dodge = 2)) +
  theme(panel.border = element_rect(color = "darkgrey", fill = NA))

7.2 Diskriminering

Code
df %>%
  select(q16:q23) %>%
  mutate(across(everything(), ~ as.numeric(.x))) %>%
  RItileplot()

q23 - “Har du känt dig illa/annorlunda behandlad på din skola i år på grund av: - Inget av ovanstående”

q23 är tvetydig, och kodas först som “Annat” i nedanstående figur.

Code
df.viz %>% 
  filter(!diskriminerad == "Ej diskriminerad") %>% 
  filter(Årskurs %in% c("Åk 7","Åk 8", "Åk 9")) %>% 
  mutaterskurs = droplevels(Årskurs),
         diskriminerad = droplevels(diskriminerad)) %>% 
  drop_na(diskriminerad) %>%
  group_by(År,Årskurs,Kön) %>%
  count(diskriminerad, .drop = F) %>%
  mutate(Andel = 100*n/sum(n)) %>%
  mutate(diskriminerad = fct_reorder(diskriminerad, desc(n))) %>%
  ggplot(data = .,
         aes(x = År, y = Andel, fill = Kön)) +
  geom_bar(position = position_dodge(),
           stat = 'identity') +
  scale_y_continuous(limits = c(0,100), breaks = c(0,20,40,60,80,100)) +
  scale_x_continuous('År', guide = guide_axis(n.dodge = 2)) +
  scale_color_gender() +
  scale_fill_gender() +
  theme_minimal() +
  theme_rise() +
  facet_grid(Årskurs~diskriminerad,
             labeller = labeller(diskriminerad = label_wrap_gen(16))) 

Vi tar bort Annat och justerar y axeln så vi bättre kan se utveckling över tid.

Code
df.viz %>% 
  filter(!diskriminerad %in% c("Ej diskriminerad","Annat")) %>% 
  filter(Årskurs %in% c("Åk 7","Åk 8", "Åk 9")) %>% 
  mutaterskurs = droplevels(Årskurs),
         diskriminerad = droplevels(diskriminerad)) %>% 
  drop_na(diskriminerad) %>%
  group_by(År,Årskurs,Kön) %>%
  count(diskriminerad, .drop = F) %>%
  mutate(Andel = 100*n/sum(n)) %>%
  mutate(diskriminerad = fct_reorder(diskriminerad, desc(n))) %>%
  ggplot(data = .,
         aes(x = År, y = Andel, fill = Kön)) +
  geom_bar(position = position_dodge(),
           stat = 'identity') +
  # scale_y_continuous(limits = c(0,100), breaks = c(0,20,40,60,80,100)) +
  scale_x_continuous('År', guide = guide_axis(n.dodge = 2)) +
  scale_color_gender() +
  scale_fill_gender() +
  theme_minimal() +
  theme_rise() +
  facet_grid(Årskurs~diskriminerad,
             labeller = labeller(diskriminerad = label_wrap_gen(16))) 

Kanske bättre med linje+punkt.

Code
df.viz %>% 
  filter(!diskriminerad %in% c("Ej diskriminerad","Annat")) %>% 
  filter(Årskurs %in% c("Åk 7","Åk 8", "Åk 9")) %>% 
  mutaterskurs = droplevels(Årskurs),
         diskriminerad = droplevels(diskriminerad)) %>% 
  drop_na(diskriminerad) %>%
  group_by(År,Årskurs,Kön) %>%
  count(diskriminerad, .drop = F) %>%
  mutate(Andel = 100*n/sum(n)) %>%
  mutate(diskriminerad = fct_reorder(diskriminerad, desc(n))) %>%
  ggplot(data = .,
         aes(x = År, y = Andel, color = Kön, group = Kön)) +
  geom_point(size = 2) +
  geom_line(linewidth = 0.9) +
# scale_y_continuous(limits = c(0,100), breaks = c(0,20,40,60,80,100)) +
  scale_x_continuous('År', guide = guide_axis(n.dodge = 2)) +
  scale_color_gender() +
  scale_fill_gender() +
  theme_minimal() +
  theme_rise() +
  facet_grid(Årskurs~diskriminerad,
             labeller = labeller(diskriminerad = label_wrap_gen(16))) 

7.3 Var känner du dig minst trygg på skolan?

Code
df.viz %>% 
  drop_na(q13,Kön) %>% 
  filter(!q13 == "Jag känner mig trygg överallt på skolan") %>% 
  ggplot(aes(x = q13, fill = Kön)) +
  geom_bar(position = "dodge") +
  # scale_y_continuous(limits = c(0,100), breaks = c(0,20,40,60,80,100)) +
  scale_x_discrete('', guide = guide_axis(n.dodge = 2),
                   labels = ~ str_wrap(.x, 8)) +
  scale_color_gender() +
  scale_fill_gender() +
  theme_minimal() +
  theme_rise() +
  labs(title = "Var känner du dig minst trygg på skolan?",
       subtitle = "Samtliga årtal",
       y = "Antal") +
  facet_wrap(~Stadium,
             ncol = 1,
             scales = "free")

Code
df.viz %>% 
  drop_na(q13,Kön) %>% 
  filter(!q13 == "Jag känner mig trygg överallt på skolan") %>% 
  ggplot(aes(x = q13, fill = Kön)) +
  geom_bar(position = "dodge") +
  # scale_y_continuous(limits = c(0,100), breaks = c(0,20,40,60,80,100)) +
  scale_x_discrete('', guide = guide_axis(n.dodge = 3),
                   labels = ~ str_wrap(.x, 8)) +
  scale_color_gender() +
  scale_fill_gender() +
  theme_minimal() +
  theme_rise() +
  labs(title = "Var känner du dig minst trygg på skolan?",
       subtitle = "Samtliga årtal",
       y = "Antal") +
  facet_wrap(~Årskurs,
             scales = "free") +
  theme(panel.border = element_rect(colour = "darkgrey", fill = NA))

Code
df.viz %>% 
  select(q13,År,Stadium,Kön,Årskurs) %>%
  group_by(År,Stadium,Kön) %>% 
  count(q13, .drop = F) %>% 
  mutate(Andel = n *100 /sum(n)) %>% 
  ungroup() %>% 
  filter(q13 == "Jag känner mig trygg överallt på skolan") %>% 
  ggplot(aes(x = År, y = Andel, color = Kön, group = Kön)) +
  geom_point(size = 2) +
  geom_line(linewidth = 0.9) +
  scale_y_continuous(limits = c(0,100), breaks = c(0,20,40,60,80,100)) +
  scale_x_continuous('År', guide = guide_axis(n.dodge = 2),
                   labels = ~ str_wrap(.x, 8)) +
  scale_color_gender() +
  scale_fill_gender() +
  theme_minimal() +
  theme_rise() +
  labs(title = "Jag känner mig trygg överallt på skolan",
       y = "Andel") +
  facet_wrap(~Stadium)

Code
df.viz %>% 
  select(q13,År,Kön,Årskurs) %>%
  filter(Årskurs == "Åk 9") %>% 
  group_by(År,Kön) %>% 
  count(q13, .drop = F) %>% 
  mutate(Andel = n *100 /sum(n)) %>% 
  ungroup() %>% 
  filter(q13 == "Jag känner mig trygg överallt på skolan") %>% 
  ggplot(aes(x = År, y = Andel, color = Kön, group = Kön)) +
  geom_point(size = 12) +
  geom_line(aes(group = Kön), linewidth = 2) +
  geom_text(aes(label = round(Andel,1)), color = "white") +
  scale_y_continuous(limits = c(0,100), breaks = c(0,20,40,60,80,100)) +
  scale_x_continuous('År', guide = guide_axis(n.dodge = 2),
                   labels = ~ str_wrap(.x, 8)) +
  scale_color_gender() +
  scale_fill_gender() +
  theme_minimal() +
  theme_rise() +
  labs(title = "Jag känner mig trygg överallt på skolan",
       subtitle = "Enbart årskurs 9",
       y = "Andel")

7.4 Jag känner mig trygg i skolan

Även Skolinspektionen använder denna fråga och båda har fyra svarskategorier. Dock är det inte exakt samma svarskategorier. SI använder Stämmer helt och hållet, Stämmer ganska bra, Stämmer ganska dåligt, Stämmer inte alls, medan TTS har Instämmer helt, 3, 2, Instämmer inte alls. Därmed är det inte säkert att de är helt jämförbara.

Code
df.viz %>% 
  select(q6,Stadium,År,Kön) %>% 
  mutate(q6 = recode(q6,"'Vet ej'=NA")) %>% 
  mutate(q6 = factor(q6, levels = c("Instämmer inte alls","2","3","Instämmer helt"))) %>% 
  drop_na(q6) %>% 
  group_by(År,Stadium,Kön) %>% 
  count(q6, .drop = F) %>% 
  mutate(Andel = n * 100 / sum(n)) %>% 
  
  ggplot(aes(x = År, y = Andel, color = q6, group = q6)) +
  geom_point(size = 2.2) +
  geom_line(linewidth = 1) +
  scale_color_manual(values = rev(c("#5C758B","#009CA6", "#B94F70", "#EC5D4F"))) +
  theme_minimal() +
  theme_rise() +
  scale_y_continuous(limits = c(0,100),
                     breaks = c(0,20,40,60,80,100)) +
  scale_x_continuous('År', guide = guide_axis(n.dodge = 2)) +
  theme(panel.border = element_rect(color = "darkgrey", fill = NA)) +
  facet_grid(Kön~Stadium)  +
  labs(title = "Jag känner mig trygg i skolan",
       subtitle = "Samtliga svarskategorier")

Code
df.viz %>% 
  select(q6,Årskurs,År,Kön) %>% 
  filter(Årskurs == "Åk 9") %>% 
  mutate(q6 = recode(q6,"'Vet ej'=NA")) %>% 
  mutate(q6 = factor(q6, levels = c("Instämmer inte alls","2","3","Instämmer helt"))) %>% 
  drop_na(q6) %>% 
  group_by(År,Kön) %>% 
  count(q6, .drop = F) %>% 
  mutate(Andel = n * 100 / sum(n)) %>% 
  
  ggplot(aes(x = År, y = Andel, color = q6, group = q6)) +
  geom_point(size = 2.2) +
  geom_line(linewidth = 1) +
  scale_color_manual(values = rev(c("#5C758B","#009CA6", "#B94F70", "#EC5D4F"))) +
  theme_minimal() +
  theme_rise() +
  scale_y_continuous(limits = c(0,100),
                     breaks = c(0,20,40,60,80,100)) +
  scale_x_continuous('År', guide = guide_axis(n.dodge = 2)) +
  theme(panel.border = element_rect(color = "darkgrey", fill = NA)) +
  facet_wrap(~Kön)  +
  labs(title = "Jag känner mig trygg i skolan",
       subtitle = "Samtliga svarskategorier - enbart åk 9")

Code
df.viz %>% 
  select(q6,Stadium,År,Kön) %>% 
  mutate(q6 = recode(q6,"'Vet ej'=NA;'2'='Instämmer inte alls';'3'='Instämmer helt'")) %>% 
  mutate(q6 = factor(q6, levels = c("Instämmer inte alls","Instämmer helt"))) %>% 
  drop_na(q6) %>% 
  group_by(År,Stadium,Kön) %>% 
  count(q6, .drop = F) %>% 
  mutate(Andel = n * 100 / sum(n)) %>% 
  
  ggplot(aes(x = År, y = Andel, color = q6, group = q6)) +
  geom_point(size = 2.2) +
  geom_line(linewidth = 1) +
  scale_color_manual(values = rev(c("#5C758B","#EC5D4F"))) +
  theme_minimal() +
  theme_rise() +
  scale_y_continuous(limits = c(0,100),
                     breaks = c(0,20,40,60,80,100)) +
  scale_x_continuous('År', 
                     guide = guide_axis(n.dodge = 2)) +
  theme(panel.border = element_rect(color = "darkgrey", fill = NA)) +
  facet_grid(Kön~Stadium)  +
  labs(title = "Jag känner mig trygg i skolan",
       subtitle = "Sammanslagna svarskategorier")

Code
df.viz %>% 
  filter(Årskurs == "Åk 9") %>% 
  select(q6,År,Kön) %>% 
  mutate(q6 = recode(q6,"'Vet ej'=NA;'2'='Instämmer inte alls';'3'='Instämmer helt'")) %>% 
  mutate(q6 = factor(q6, levels = c("Instämmer inte alls","Instämmer helt"))) %>% 
  drop_na(q6) %>% 
  group_by(År,Kön) %>% 
  count(q6, .drop = F) %>% 
  mutate(Andel = n * 100 / sum(n)) %>% 
  
  ggplot(aes(x = År, y = Andel, color = q6, group = q6)) +
  geom_point(size = 8) +
  geom_line(linewidth = 2) +
  geom_text(aes(label = round(Andel,1)), color = "white",
            size = 3) +
  scale_color_manual(values = c("#EC5D4F","#5C758B"),
                     labels = c("Instämmer inte","Instämmer")) +
  theme_minimal() +
  theme_rise() +
  scale_y_continuous(limits = c(0,100),
                     breaks = c(0,20,40,60,80,100)) +
  scale_x_continuous('År', 
                     guide = guide_axis(n.dodge = 2)) +
  theme(panel.border = element_rect(color = "darkgrey", fill = NA)) +
  facet_wrap(~Kön)  +
  labs(title = "Jag känner mig trygg i skolan",
       subtitle = "Sammanslagna svarskategorier (positiva/negativa)\nEnbart åk 9") +
  coord_cartesian(clip = "off")

Code
df.viz %>% 
  select(q6,Skola,År,Kön) %>% 
  mutate(q6 = recode(q6,"'Vet ej'=NA")) %>% 
  mutate(q6 = factor(q6, levels = c("Instämmer inte alls","2","3","Instämmer helt"))) %>% 
  drop_na(q6) %>% 
  group_by(År,Skola,Kön) %>% 
  count(q6, .drop = F) %>% 
  mutate(Andel = n * 100 / sum(n)) %>% 
  ungroup() %>% 
  mutate(Skola = str_replace(Skola,"skolan","- skolan")) %>% 
  
  ggplot(aes(x = År, y = Andel, color = q6, group = q6)) +
  geom_point(size = 2.2) +
  geom_line(linewidth = 1) +
  scale_color_manual('',
                     values = rev(c("#5C758B","#009CA6", "#B94F70", "#EC5D4F"))) +
  theme_minimal() +
  theme_rise() +
  scale_y_continuous(limits = c(0,100),
                     breaks = c(0,20,40,60,80,100)) +
  scale_x_continuous('År', guide = guide_axis(n.dodge = 2)) +
  theme(panel.border = element_rect(color = "darkgrey", fill = NA),
        legend.position = "top") +
  facet_grid(Kön~Skola,
             labeller = labeller(Skola = label_wrap_gen(4)))  +
  labs(title = "Jag känner mig trygg i skolan",
       subtitle = "Samtliga svarskategorier")

Code
df.viz %>% 
  select(q6,Skola,År,Kön) %>% 
  mutate(q6 = recode(q6,"'Vet ej'=NA;'2'='Instämmer inte alls';'3'='Instämmer helt'")) %>% 
  mutate(q6 = factor(q6, levels = c("Instämmer inte alls","Instämmer helt"))) %>% 
  drop_na(q6) %>% 
  group_by(År,Skola,Kön) %>% 
  count(q6, .drop = F) %>% 
  mutate(Andel = n * 100 / sum(n)) %>% 
  ungroup() %>% 
  mutate(Skola = str_replace(Skola,"skolan","- skolan")) %>% 
  
  ggplot(aes(x = År, y = Andel, color = q6, group = q6)) +
  geom_point(size = 2.2) +
  geom_line(linewidth = 1) +
  scale_color_manual('',values = rev(c("#5C758B","#EC5D4F"))) +
  theme_minimal() +
  theme_rise() +
  scale_y_continuous(limits = c(0,100),
                     breaks = c(0,20,40,60,80,100)) +
  scale_x_continuous('År', guide = guide_axis(n.dodge = 2)) +
  theme(panel.border = element_rect(color = "darkgrey", fill = NA),
        legend.position = "top") +
  facet_grid(Kön~Skola,
             labeller = labeller(Skola = label_wrap_gen(4)))  +
  labs(title = "Jag känner mig trygg i skolan",
       subtitle = "Sammanslagna svarskategorier")

7.5 Jag trivs i skolan

Code
df.viz %>% 
  select(q1,Stadium,År,Kön) %>% 
  mutate(q1 = recode(q1,"'Vet ej'=NA")) %>% 
  mutate(q1 = factor(q1, levels = c("Instämmer inte alls","2","3","Instämmer helt"))) %>%   drop_na(q1) %>% 
  group_by(År,Stadium,Kön) %>% 
  count(q1, .drop = F) %>% 
  mutate(Andel = n * 100 / sum(n)) %>%
  
  ggplot(aes(x = År, y = Andel, color = q1, group = q1)) +
  geom_point(size = 2.2) +
  geom_line(linewidth = 1) +
  scale_color_manual(values = rev(c("#5C758B","#009CA6", "#B94F70", "#EC5D4F"))) +
  theme_minimal() +
  theme_rise() +
  scale_y_continuous(limits = c(0,100),
                     breaks = c(0,20,40,60,80,100)) +
  scale_x_continuous('År', guide = guide_axis(n.dodge = 2)) +
  theme(panel.border = element_rect(color = "darkgrey", fill = NA)) +
  facet_grid(Kön~Stadium) +
  labs(title = "Jag trivs i skolan",
       subtitle = "Samtliga svarskategorier")

Code
df.viz %>% 
  select(q1,Årskurs,År,Kön) %>% 
  filter(Årskurs == "Åk 9") %>% 
  mutate(q1 = recode(q1,"'Vet ej'=NA")) %>% 
  mutate(q1 = factor(q1, levels = c("Instämmer inte alls","2","3","Instämmer helt"))) %>%   drop_na(q1) %>% 
  group_by(År,Kön) %>% 
  count(q1, .drop = F) %>% 
  mutate(Andel = n * 100 / sum(n)) %>%
  
  ggplot(aes(x = År, y = Andel, color = q1, group = q1)) +
  geom_point(size = 2.2) +
  geom_line(linewidth = 1) +
  scale_color_manual(values = rev(c("#5C758B","#009CA6", "#B94F70", "#EC5D4F"))) +
  theme_minimal() +
  theme_rise() +
  scale_y_continuous(limits = c(0,100),
                     breaks = c(0,20,40,60,80,100)) +
  scale_x_continuous('År', guide = guide_axis(n.dodge = 2)) +
  theme(panel.border = element_rect(color = "darkgrey", fill = NA)) +
  facet_wrap(~Kön) +
  labs(title = "Jag trivs i skolan",
       subtitle = "Samtliga svarskategorier - enbart åk 9")

Code
df.viz %>% 
  select(q1,Stadium,År,Kön) %>% 
  mutate(q1 = recode(q1,"'Vet ej'=NA;'2'='Instämmer inte alls';'3'='Instämmer helt'")) %>% 
  mutate(q1 = factor(q1, levels = c("Instämmer inte alls","Instämmer helt"))) %>% 
  drop_na(q1) %>% 
  group_by(År,Stadium,Kön) %>% 
  count(q1, .drop = F) %>% 
  mutate(Andel = n * 100 / sum(n)) %>% 
  
  ggplot(aes(x = År, y = Andel, color = q1, group = q1)) +
  geom_point(size = 2.2) +
  geom_line(linewidth = 1) +
  scale_color_manual(values = rev(c("#5C758B","#EC5D4F"))) +
  theme_minimal() +
  theme_rise() +
  scale_y_continuous(limits = c(0,100),
                     breaks = c(0,20,40,60,80,100)) +
  scale_x_continuous('År', 
                     guide = guide_axis(n.dodge = 2)) +
  theme(panel.border = element_rect(color = "darkgrey", fill = NA)) +
  facet_grid(Kön~Stadium) +
  labs(title = "Jag trivs i skolan",
       subtitle = "Sammanslagna svarskategorier")

Code
df.viz %>% 
  filter(Årskurs == "Åk 9") %>% 
  select(q1,År,Kön) %>% 
  mutate(q1 = recode(q1,"'Vet ej'=NA;'2'='Instämmer inte alls';'3'='Instämmer helt'")) %>% 
  mutate(q1 = factor(q1, levels = c("Instämmer inte alls","Instämmer helt"))) %>% 
  drop_na(q1) %>% 
  group_by(År,Kön) %>% 
  count(q1, .drop = F) %>% 
  mutate(Andel = n * 100 / sum(n)) %>% 
  
  ggplot(aes(x = År, y = Andel, color = q1, group = q1)) +
  geom_point(size = 8) +
  geom_line(linewidth = 2) +
  geom_text(aes(label = round(Andel,1)), color = "white",
            size = 3) +
  scale_color_manual(values = c("#EC5D4F","#5C758B"),
                     labels = c("Instämmer inte","Instämmer")) +
  #scale_color_manual(values = rev(c("#5C758B","#EC5D4F"))) +
  theme_minimal() +
  theme_rise() +
  scale_y_continuous(limits = c(0,100),
                     breaks = c(0,20,40,60,80,100)) +
  scale_x_continuous('År', 
                     guide = guide_axis(n.dodge = 2)) +
  theme(panel.border = element_rect(color = "darkgrey", fill = NA)) +
  facet_wrap(~Kön) +
  labs(title = "Jag trivs i skolan",
       subtitle = "Sammanslagna svarskategorier (positiva/negativa)\nEnbart årskurs 9")

Code
df.viz %>% 
  select(q1,Skola,År,Kön) %>% 
  mutate(q1 = recode(q1,"'Vet ej'=NA")) %>% 
  mutate(q1 = factor(q1, levels = c("Instämmer inte alls","2","3","Instämmer helt"))) %>% 
  drop_na(q1) %>% 
  group_by(År,Skola,Kön) %>% 
  count(q1, .drop = F) %>% 
  mutate(Andel = n * 100 / sum(n)) %>% 
  ungroup() %>% 
  mutate(Skola = str_replace(Skola,"skolan","- skolan")) %>% 
  
  ggplot(aes(x = År, y = Andel, color = q1, group = q1)) +
  geom_point(size = 2.2) +
  geom_line(linewidth = 1) +
  scale_color_manual('',
                     values = rev(c("#5C758B","#009CA6", "#B94F70", "#EC5D4F"))) +
  theme_minimal() +
  theme_rise() +
  scale_y_continuous(limits = c(0,100),
                     breaks = c(0,20,40,60,80,100)) +
  scale_x_continuous('År', guide = guide_axis(n.dodge = 2)) +
  theme(panel.border = element_rect(color = "darkgrey", fill = NA),
        legend.position = "top") +
  facet_grid(Kön~Skola,
             labeller = labeller(Skola = label_wrap_gen(4)))  +
  labs(title = "Jag trivs i skolan",
       subtitle = "Samtliga svarskategorier")

Code
df.viz %>% 
  select(q1,Skola,År,Kön) %>% 
  mutate(q1 = recode(q1,"'Vet ej'=NA;'2'='Instämmer inte alls';'3'='Instämmer helt'")) %>% 
  mutate(q1 = factor(q1, levels = c("Instämmer inte alls","Instämmer helt"))) %>% 
  drop_na(q1) %>% 
  group_by(År,Skola,Kön) %>% 
  count(q1, .drop = F) %>% 
  mutate(Andel = n * 100 / sum(n)) %>% 
  ungroup() %>% 
  mutate(Skola = str_replace(Skola,"skolan","- skolan")) %>% 
  
  ggplot(aes(x = År, y = Andel, color = q1, group = q1)) +
  geom_point(size = 2.2) +
  geom_line(linewidth = 1) +
  scale_color_manual('',values = rev(c("#5C758B","#EC5D4F"))) +
  theme_minimal() +
  theme_rise() +
  scale_y_continuous(limits = c(0,100),
                     breaks = c(0,20,40,60,80,100)) +
  scale_x_continuous('År', guide = guide_axis(n.dodge = 2)) +
  theme(panel.border = element_rect(color = "darkgrey", fill = NA),
        legend.position = "top") +
  facet_grid(Kön~Skola,
             labeller = labeller(Skola = label_wrap_gen(4)))  +
  labs(title = "Jag trivs i skolan",
       subtitle = "Sammanslagna svarskategorier")

7.6 Indexvärde TTS

Code
df.viz.index <- df %>% 
  filter(!Stadium == "Förskoleklass") %>% 
  mutate(Kön = car::recode(Kön,"'Annat/Vill ej svara'=NA")) %>% 
  mutate(across(q1:q10, ~ car::recode(.x,"'Instämmer inte alls'=0;
                                            2=1;
                                            3=2;
                                            'Instämmer helt'=3;
                                            'Vet ej'=NA;
                                            '<NA>'=NA",
                                            as.factor = FALSE))) %>%
  mutate(q10 = recode(q10,"1=0;2=1;3=1"), as.factor=F) %>% 
  select(all_of(names(df.index)),diskriminerad,Kön,Årskurs,Stadium,År,Skola) %>% 
  mutaterskurs = droplevels(Årskurs),
         Stadium = droplevels(Stadium)) %>% 
  mutate(Stadium = factor(Stadium, levels = 
                            c("Lågstadiet","Mellanstadiet","Högstadiet"))) %>% 
  na.omit()
Code
df.viz.index$score <- RIestThetas2(df.viz.index %>% 
                                     select(starts_with("q")), cpu = 8)

#write_parquet(df.viz.index,"TTSscored.parquet")
Code
df.viz.index <- read_parquet("TTSscored.parquet")
Code
RIlistItemsMargin(df.index)
itemnr item
q2 Jag trivs i min klass
q3 Jag kan koncentrera mig på lektionerna
q5 Jag lär mig olika saker på lektionerna
q7 Jag blir bra bemött av vuxna i skolan
q8 Jag blir bra bemött av elever i skolan
q9 De vuxna på skolan reagerar om de får reda på att någon har varit elak mot en elev
q10 Jag vet vem på skolan jag kan vända mig till om någon är elak mot mig
Code
df.viz.index %>%
    group_by(År, Stadium, Kön) %>%
    reframe(
      Medel = mean(score, na.rm = T), # calculate averages, etc
      StDev = sd(score, na.rm = T),
      n = n(),
      StErr = StDev / sqrt(n),
      sd.lo = Medel - StDev,
      sd.hi = Medel + StDev
    ) %>%
    mutate(across(where(is.numeric), ~ round(.x, 3))) %>%
  ggplot(aes(x = År, y = Medel, group = Kön, color = Kön)) + # make plot, with area color
  geom_line(linewidth = 1) +
  geom_point(size = 2.5) +
  # geom_line(data = filter(df.plot, Kommun == fokusKommun),
  #           alpha = 0.9, linewidth = 1.2,) +
  # geom_point(data = filter(df.plot, Kommun == fokusKommun),
  #            alpha = 0.9, size = 3) +
  geom_ribbon(aes(ymin = sd.lo, ymax = sd.hi, fill = Kön),
              alpha = 0.05, linetype = 0
  ) +
  #scale_y_continuous(limits = xlim) +
  scale_x_continuous(guide = guide_axis(n.dodge = 2)) +
  scale_color_gender() +
  scale_fill_gender() +
  labs(title = "Medelvärde över tid",
       subtitle = "Skuggat fält indikerar en standardavvikelse (~ 68%)") +
  xlab("Årtal") +
  theme_minimal() +
  theme_rise() +
  theme(panel.spacing = unit(0.7, "cm")) +
  facet_wrap(~Stadium) +
  coord_cartesian(ylim = c(-2,4.5))

Code
df.viz.index %>%
    group_by(År, Årskurs, Kön) %>%
    reframe(
      Medel = mean(score, na.rm = T), # calculate averages, etc
      StDev = sd(score, na.rm = T),
      n = n(),
      StErr = StDev / sqrt(n),
      sd.lo = Medel - StDev,
      sd.hi = Medel + StDev
    ) %>%
    mutate(across(where(is.numeric), ~ round(.x, 3))) %>%
    #mutate(Skola = str_replace(Skola,"skolan","- skolan")) %>% 
  ggplot(aes(x = År, y = Medel, group = Kön, color = Kön)) + # make plot, with area color
  geom_line(linewidth = 1) +
  geom_point(size = 2.5) +
  # geom_line(data = filter(df.plot, Kommun == fokusKommun),
  #           alpha = 0.9, linewidth = 1.2,) +
  # geom_point(data = filter(df.plot, Kommun == fokusKommun),
  #            alpha = 0.9, size = 3) +
  geom_ribbon(aes(ymin = sd.lo, ymax = sd.hi, fill = Kön),
              alpha = 0.05, linetype = 0
  ) +
  #scale_y_continuous(limits = xlim) +
  scale_x_continuous(guide = guide_axis(n.dodge = 2)) +
  scale_color_gender() +
  scale_fill_gender() +
  labs(title = "Medelvärde över tid",
       subtitle = "Skuggat fält indikerar en standardavvikelse (~ 68%)") +
  xlab("Årtal") +
  theme_minimal() +
  theme_rise() +
  theme(panel.spacing = unit(0.7, "cm")) +
  facet_wrap(~Årskurs,
             nrow = 3) +
  coord_cartesian(ylim = c(-2,4.5))

Code
df.viz.index %>%
  filter(Årskurs == "Åk 9") %>% 
    group_by(År, Kön) %>%
    reframe(
      Medel = mean(score, na.rm = T), # calculate averages, etc
      StDev = sd(score, na.rm = T),
      n = n(),
      StErr = StDev / sqrt(n),
      sd.lo = Medel - StDev,
      sd.hi = Medel + StDev
    ) %>%
    mutate(across(where(is.numeric), ~ round(.x, 3))) %>%
  ggplot(aes(x = År, y = Medel, group = Kön, color = Kön)) + # make plot, with area color
  geom_point(size = 8) +
  geom_line(linewidth = 2) +
  geom_text(aes(label = round(Medel,2)), color = "white",
            size = 3) +
  geom_ribbon(aes(ymin = sd.lo, ymax = sd.hi, fill = Kön),
              alpha = 0.08, linetype = 0
  ) +
  scale_x_continuous(guide = guide_axis(n.dodge = 2)) +
  scale_color_gender() +
  scale_fill_gender() +
  labs(title = "Medelvärde över tid - årskurs 9",
       subtitle = "Skuggat fält indikerar en standardavvikelse (~ 68%)") +
  xlab("Årtal") +
  theme_minimal() +
  theme_rise() +
  theme(panel.spacing = unit(0.7, "cm"))

Code
df.viz.index %>%
  filter(Årskurs == "Åk 9") %>% 
  mutater = factor(År)) %>% 

  ggplot(aes(x = År, y = score, group = År)) +
  geom_jitter(shape = 16, 
              position = 
                position_jitter(0.3), 
              alpha = 0.15) +
  geom_violin(aes(fill = År), 
              alpha= 0.85) +
  geom_boxplot(width = .3, 
               outlier.shape = NA, 
               alpha = 0.3, 
               notch = TRUE) +
  scale_x_discrete(guide = guide_axis(n.dodge = 1)) +
  scale_color_brewer() +
  scale_fill_brewer() +
  labs(title = "TTS fördelning av mätvärden över tid",
       subtitle = "Årskurs 9",
       y = "TTS-indexvärde") +
  xlab("Årtal") +
  theme_minimal() +
  theme_rise() +
  theme(panel.spacing = unit(0.7, "cm"),
        legend.position = "none") +
  facet_wrap(~Kön)

Code
df.viz.index %>%
    group_by(År, Skola, Kön) %>%
    reframe(
      Medel = mean(score, na.rm = T), # calculate averages, etc
      StDev = sd(score, na.rm = T),
      n = n(),
      StErr = StDev / sqrt(n),
      sd.lo = Medel - StDev,
      sd.hi = Medel + StDev
    ) %>%
    mutate(across(where(is.numeric), ~ round(.x, 3))) %>%
  mutate(Skola = str_replace(Skola,"skolan","- skolan")) %>% 

  ggplot(aes(x = År, y = Medel, group = Kön, color = Kön)) + # make plot, with area color
  geom_line(linewidth = 1) +
  geom_point(size = 2.5) +
  # geom_line(data = filter(df.plot, Kommun == fokusKommun),
  #           alpha = 0.9, linewidth = 1.2,) +
  # geom_point(data = filter(df.plot, Kommun == fokusKommun),
  #            alpha = 0.9, size = 3) +
  # geom_ribbon(aes(ymin = sd.lo, ymax = sd.hi, fill = Kön),
  #             alpha = 0.05, linetype = 0
  # ) +
  #scale_y_continuous(limits = xlim) +
  scale_x_continuous(guide = guide_axis(n.dodge = 2)) +
  scale_color_gender() +
  scale_fill_gender() +
  labs(title = "Medelvärde över tid") +
  xlab("Årtal") +
  theme_minimal() +
  theme_rise() +
  theme(panel.spacing = unit(0.7, "cm")) +
  facet_wrap(~Skola,
             nrow = 1,
             labeller = labeller(Skola = label_wrap_gen(4))) +
  coord_cartesian(ylim = c(-2,4.5))

Code
df.viz.index %>%
  filter(Stadium == "Högstadiet") %>% 
    group_by(År, Skola, Kön) %>%
    reframe(
      Medel = mean(score, na.rm = T), # calculate averages, etc
      StDev = sd(score, na.rm = T),
      n = n(),
      StErr = StDev / sqrt(n),
      sd.lo = Medel - StDev,
      sd.hi = Medel + StDev
    ) %>%
    mutate(across(where(is.numeric), ~ round(.x, 3))) %>%
    mutate(Skola = str_replace(Skola,"skolan","- skolan")) %>% 
  ggplot(aes(x = År, y = Medel, group = Kön, color = Kön)) + # make plot, with area color
  geom_line(linewidth = 1) +
  geom_point(size = 2.5) +
  # geom_line(data = filter(df.plot, Kommun == fokusKommun),
  #           alpha = 0.9, linewidth = 1.2,) +
  # geom_point(data = filter(df.plot, Kommun == fokusKommun),
  #            alpha = 0.9, size = 3) +
  # geom_ribbon(aes(ymin = sd.lo, ymax = sd.hi, fill = Kön),
  #             alpha = 0.05, linetype = 0
  # ) +
  #scale_y_continuous(limits = xlim) +
  scale_x_continuous(guide = guide_axis(n.dodge = 2)) +
  scale_color_gender() +
  scale_fill_gender() +
  labs(title = "Medelvärde över tid") +
  xlab("Årtal") +
  theme_minimal() +
  theme_rise() +
  theme(panel.spacing = unit(0.7, "cm")) +
  facet_wrap(~Skola,
             nrow = 1,
             labeller = labeller(Skola = label_wrap_gen(4))) +
  coord_cartesian(ylim = c(-2,4.5))

Code
df.viz.index %>%
  filter(Stadium == "Mellanstadiet") %>% 
    group_by(År, Skola, Kön) %>%
    reframe(
      Medel = mean(score, na.rm = T), # calculate averages, etc
      StDev = sd(score, na.rm = T),
      n = n(),
      StErr = StDev / sqrt(n),
      sd.lo = Medel - StDev,
      sd.hi = Medel + StDev
    ) %>%
    mutate(across(where(is.numeric), ~ round(.x, 3))) %>%
    mutate(Skola = str_replace(Skola,"skolan","- skolan")) %>% 
  ggplot(aes(x = År, y = Medel, group = Kön, color = Kön)) + # make plot, with area color
  geom_line(linewidth = 1) +
  geom_point(size = 2.5) +
  # geom_line(data = filter(df.plot, Kommun == fokusKommun),
  #           alpha = 0.9, linewidth = 1.2,) +
  # geom_point(data = filter(df.plot, Kommun == fokusKommun),
  #            alpha = 0.9, size = 3) +
  # geom_ribbon(aes(ymin = sd.lo, ymax = sd.hi, fill = Kön),
  #             alpha = 0.05, linetype = 0
  # ) +
  #scale_y_continuous(limits = xlim) +
  scale_x_continuous(guide = guide_axis(n.dodge = 2)) +
  scale_color_gender() +
  scale_fill_gender() +
  labs(title = "Medelvärde över tid") +
  xlab("Årtal") +
  theme_minimal() +
  theme_rise() +
  theme(panel.spacing = unit(0.7, "cm")) +
  facet_wrap(~Skola,
             nrow = 1,
             labeller = labeller(Skola = label_wrap_gen(4))) +
  coord_cartesian(ylim = c(-2,4.5))

Code
df.viz.index %>%
  filter(Stadium == "Lågstadiet") %>% 
    group_by(År, Skola, Kön) %>%
    reframe(
      Medel = mean(score, na.rm = T), # calculate averages, etc
      StDev = sd(score, na.rm = T),
      n = n(),
      StErr = StDev / sqrt(n),
      sd.lo = Medel - StDev,
      sd.hi = Medel + StDev
    ) %>%
    mutate(across(where(is.numeric), ~ round(.x, 3))) %>%
    mutate(Skola = str_replace(Skola,"skolan","- skolan")) %>% 

  ggplot(aes(x = År, y = Medel, group = Kön, color = Kön)) + # make plot, with area color
  geom_line(linewidth = 1) +
  geom_point(size = 2.5) +
  # geom_line(data = filter(df.plot, Kommun == fokusKommun),
  #           alpha = 0.9, linewidth = 1.2,) +
  # geom_point(data = filter(df.plot, Kommun == fokusKommun),
  #            alpha = 0.9, size = 3) +
  # geom_ribbon(aes(ymin = sd.lo, ymax = sd.hi, fill = Kön),
  #             alpha = 0.05, linetype = 0
  # ) +
  #scale_y_continuous(limits = xlim) +
  scale_x_continuous(guide = guide_axis(n.dodge = 2)) +
  scale_color_gender() +
  scale_fill_gender() +
  labs(title = "Medelvärde över tid") +
  xlab("Årtal") +
  theme_minimal() +
  theme_rise() +
  theme(panel.spacing = unit(0.7, "cm")) +
  facet_wrap(~Skola,
             nrow = 1,
             labeller = labeller(Skola = label_wrap_gen(4))) +
  coord_cartesian(ylim = c(-2,4.5))

8 Utsatt för kränkningar

Fråga q11 och q12, hur stor andel svarar nej? För jämförbarhet med Stockholmsenkätens fråga om mobbning.

  • q11: “Har du varit utsatt för kränkning på din skola i år?”
  • q12: “Har du varit utsatt för upprepade kränkningar på din skola i år?”
Code
df %>% 
  filter(Årskurs == "Åk 9") %>% 
  select(År,Kön,q11) %>% 
  group_by(År,Kön,q11) %>% 
  summarise(Antal = n()) %>% 
  mutate(Andel = round(100 * Antal/sum(Antal),1),
         År = factor(År)) %>% 
  filter(q11 == "Nej") %>% 
  
  ggplot(aes(x = År,
             y = Andel,
             group = Kön,
             color = Kön)
         ) +
  geom_point(size = 10) +
  geom_line(linewidth = 2) +
  geom_text(aes(label = round(Andel,1)), 
            color = "white",
            size = 3) +
  scale_y_continuous('Andel i %', limits = c(0,100), breaks = c(0,20,40,60,80,100)) +
  scale_x_discrete('År', guide = guide_axis(n.dodge = 1)) +
  scale_color_viridis_d(begin = 0.2,
                        end = 0.85) +
  theme_minimal() +
  theme_rise() +
  labs(title = "'Har du varit utsatt för kränkning på din skola i år?'",
       subtitle = "Andel som svarar 'Nej' i årskurs 9")

Code
df %>% 
  filter(Årskurs == "Åk 9") %>% 
  select(År,Kön,q12) %>% 
  group_by(År,Kön,q12) %>% 
  summarise(Antal = n()) %>% 
  mutate(Andel = round(100 * Antal/sum(Antal),1),
         År = factor(År)) %>% 
  filter(q12 == "Nej") %>% 
  
  ggplot(aes(x = År,
             y = Andel,
             group = Kön,
             color = Kön)
         ) +
  geom_point(size = 10) +
  geom_line(linewidth = 2) +
  geom_text(aes(label = round(Andel,1)), 
            color = "white",
            size = 3) +
  scale_y_continuous('Andel i %', limits = c(0,100), breaks = c(0,20,40,60,80,100)) +
  scale_x_discrete('År', guide = guide_axis(n.dodge = 1)) +
  scale_color_viridis_d(begin = 0.2,
                        end = 0.85) +
  theme_minimal() +
  theme_rise() +
  labs(title = str_wrap("'Har du varit utsatt för upprepade kränkningar på din skola i år?'"),
       subtitle = "Andel som svarar 'Nej' i årskurs 9")

Code
df %>% 
  filter(Årskurs == "Åk 9") %>% 
  select(År,Kön,q11) %>% 
  group_by(År,Kön,q11) %>% 
  summarise(Antal = n()) %>% 
  mutate(Andel = round(100 * Antal/sum(Antal),1),
         År = factor(År)) %>% 
  filter(q11 == "Nej") %>% 
  
  ggplot(aes(x = År,
             y = Antal,
             group = Kön,
             color = Kön)
         ) +
  geom_point(size = 10) +
  geom_line(linewidth = 2) +
  geom_text(aes(label = round(Antal,1)), 
            color = "white",
            size = 3) +
  scale_x_discrete('År', guide = guide_axis(n.dodge = 1)) +
  scale_color_viridis_d(begin = 0.2,
                        end = 0.85) +
  theme_minimal() +
  theme_rise() +
  labs(title = "'Har du varit utsatt för kränkning på din skola i år?'",
       subtitle = "Antal som svarar 'Nej' i årskurs 9")

Code
df %>% 
  filter(Årskurs == "Åk 9") %>% 
  select(År,Kön,q12) %>% 
  group_by(År,Kön,q12) %>% 
  summarise(Antal = n()) %>% 
  mutate(Andel = round(100 * Antal/sum(Antal),1),
         År = factor(År)) %>% 
  filter(q12 == "Nej") %>% 
  
  ggplot(aes(x = År,
             y = Antal,
             group = Kön,
             color = Kön)
         ) +
  geom_point(size = 10) +
  geom_line(linewidth = 2) +
  geom_text(aes(label = round(Antal,1)), 
            color = "white",
            size = 3) +
  scale_x_discrete('År', guide = guide_axis(n.dodge = 1)) +
  scale_color_viridis_d(begin = 0.2,
                        end = 0.85) +
  theme_minimal() +
  theme_rise() +
  labs(title = str_wrap("'Har du varit utsatt för upprepade kränkningar på din skola i år?'"),
       subtitle = "Antal som svarar 'Nej' i årskurs 9")

9 Skolinspektionen/KOLADA

Code
df.si.kolada <- read_parquet("2023-05-19_KOLADA_skolinsp_tryggNöjd.parquet")

si.kolada.sigtuna <- df.si.kolada %>% 
  filter(Kommun == "Sigtuna",
         str_detect(KPI,"^Elever i åk 8"))

9.1 Manuellt hämtad data

https://www.kolada.se/verktyg/fri-sokning/?kpis=96323,171507&years=30200,30199,30198&municipals=16559&rows=municipal,kpi,gender&visualization=bar-chart

För Sigtuna finns det i KOLADA bara data från Skolinspektionens skolenkät åk 8 från år 2021 och åk 9 från år 2023.

Code
sigtuna.si <- data.frame(
  stringsAsFactors = FALSE,
       check.names = FALSE,
         Nyckeltal = c("Elever i åk 8: Känner du dig trygg i skolan? Andel som svarat \"Helt och hållet\" eller \"Till stor del\", (%)",
                       "Elever i åk 8: Känner du dig trygg i skolan? Andel som svarat \"Helt och hållet\" eller \"Till stor del\", (%)",
                       "Elever i åk 8: Känner du dig trygg i skolan? Andel som svarat \"Helt och hållet\" eller \"Till stor del\", (%)",
                       "Elever i åk 9: Jag känner mig trygg i skolan, positiva svar, andel (%) (-2021)",
                       "Elever i åk 9: Jag känner mig trygg i skolan, positiva svar, andel (%) (-2021)",
                       "Elever i åk 9: Jag känner mig trygg i skolan, positiva svar, andel (%) (-2021)"),
            Område = c("Sigtuna","Sigtuna","Sigtuna",
                       "Sigtuna","Sigtuna","Sigtuna"),
               Kön = c("Totalt", "Flicka", "Pojke", "Totalt", "Flicka", "Pojke"),
     År = c(2023, 2023, 2023, 2021, 2021, 2021),
             Värde = c(81.56996587, 75, 88, 76.8, 73, 81)
)


sigtuna.si %>% 
  filter(!Kön == "Totalt") %>% 
  mutater = factor(År)) %>% 
  ggplot(aes(x = År, y = Värde, color = Kön)) +
  geom_point(size = 12) +
  geom_line(aes(group = Kön), linewidth = 2) +
  geom_text(aes(label = round(Värde,1)), color = "white") +
  scale_y_continuous(limits = c(0,100), breaks = c(0,20,40,60,80,100)) +
  scale_x_continuous(breaks = årtal, 
                     minor_breaks = NULL) +
  scale_x_discrete(guide = guide_axis(n.dodge = 1)) +
  scale_color_gender() +
  scale_fill_gender() +
  labs(title = "'Jag känner mig trygg i skolan'",
       subtitle = "Andel positiva svar (åk 8 2021 och åk 9 2023)",
       y = "Procent") +
  xlab("Årtal") +
  theme_minimal() +
  theme_rise()

10 Jämförelser Stockholmsenkäten

Code
library(arrow)
df.sthlm <- read_parquet("../DIDapp/data/2023-05-07_ScoredRev.parquet") %>%
  filter(DIDkommun == "Sigtuna")

Vi vill titta på jämförbara frågor, där frågeställning och svarsalternativ är rimligt lika.

I Stockholmsenkäten har vi:

  • f54b Jag trivs bra i skolan.
  • f54l Vuxna ingriper om någon blir trakasserad eller mobbad.

Med svarskategorierna:

  • ‘Stämmer mycket dåligt’
  • ‘Stämmer ganska dåligt’
  • ‘Stämmer ganska bra’
  • ‘Stämmer mycket bra’

Dock har f54b fått de två negativa svarskategorierna sammanslagna eftersom det psykometriskt inte var någon skillnad mellan dem. För enkelhetens skull gör vi samma för samtliga frågor vi ska titta närmare på. De två positiva svaren hålls fortfarande åtskilda.

TTS har:

  • q1 Jag trivs i skolan
  • q9 De vuxna på skolan reagerar om de får reda på att någon har varit elak mot en elev

Också med fyra svarskategorier, där bara ändpunkterna har etiketter (Instämmer helt - Instämmer inte alls). Mittenalternativen etiketteras med siffrorna 2 och 3. Notera att detta är dålig enkätmetodik. Alla svarskategorier bör ha tydliga etiketter så respondenten kan skilja dem åt och veta vad man svarar.

Vi kodar om etiketterna på svarsalternativen i TTS så de blir samma som Stockholmsenkätens etiketter för att kunna skapa jämförande figurer.

Code
df.tts.comp <- df %>% 
  mutate(across(all_of(c("q1","q9")), ~ car::recode(.x,"'Instämmer helt'='Stämmer mycket bra';
                                      '3'='Stämmer ganska bra';
                                      '2'='Stämmer dåligt';
                                      'Instämmer inte alls'='Stämmer dåligt';
                                      'Vet ej'=NA")
                ))

df.sthlm$f54b <- recode(df.sthlm$f54b,"2='Stämmer dåligt';
                        1='Stämmer ganska bra';
                        0='Stämmer mycket bra'", as.factor = TRUE)
df.sthlm$f54l <- recode(df.sthlm$f54l,"2:3='Stämmer dåligt';
                        1='Stämmer ganska bra';
                        0='Stämmer mycket bra'", as.factor = TRUE)

Sedan behöver vi räkna på andelar över tid. Det är även intressant att jämföra antalet respondenter.

Code
df.tts.comp <- df.tts.comp %>% 
  filter(Kön %in% c("Pojke","Flicka"),
         Årskurs == "Åk 9") %>%
  select(År,Kön,q1,q9) %>% 
  pivot_longer(cols = c("q1","q9"),
               values_to = "svarskategori",
               names_to = "itemnr") %>% 
  group_by(År,Kön,itemnr) %>% 
  count(svarskategori) %>% 
  mutate(Andel = round(100 * n / sum(n),1))

df.sthlm.comp <- df.sthlm %>% 
  filter(Kön %in% c("Pojke","Flicka"),
         ARSKURS == "Åk 9") %>%
  renamer = ar,
         Årskurs = ARSKURS) %>% 
  select(År,Kön,f54b,f54l) %>% 
  pivot_longer(cols = c("f54b","f54l"),
               values_to = "svarskategori",
               names_to = "itemnr") %>% 
  group_by(År,Kön,itemnr) %>% 
  count(svarskategori) %>% 
  mutate(Andel = round(100 * n / sum(n),1))

df.comp <- rbind(df.tts.comp,df.sthlm.comp)

sthlm_labels <- data.frame(
  itemnr = c("f54l","f54b"),
  item = c("Vuxna ingriper om någon blir trakasserad eller mobbad (STHLM)",
           "Jag trivs bra i skolan (STHLM)")
)

itemlabels <- rbind(itemlabels,sthlm_labels)

itemlabels$item <- recode(itemlabels$item,"'Jag trivs i skolan'='Jag trivs i skolan (TTS)';'De vuxna på skolan reagerar om de får reda på att någon har varit elak mot en elev'='De vuxna på skolan reagerar om de får reda på att någon har varit elak mot en elev (TTS)'")

df.comp <- left_join(df.comp,itemlabels, 
                     by = "itemnr")

10.1 Antal/andel saknade svar över tid

Code
df.comp %>% 
  mutater = factor(År),
         item = factor(item)) %>%
  mutate(item = fct_relevel(item, 'Jag trivs i skolan (TTS)',
                            "Jag trivs bra i skolan (STHLM)",
                            'De vuxna på skolan reagerar om de får reda på att någon har varit elak mot en elev (TTS)',
                            "Vuxna ingriper om någon blir trakasserad eller mobbad (STHLM)")) %>% 
  rename(Antal = n) %>% 
  filter(is.na(svarskategori)) %>% 
  ggplot(aes(x = År,
             y = Antal,
             color = Kön, group = Kön)) +
  geom_point(size = 3) +
  geom_line(linewidth = 1.1) +
  scale_color_gender() +
  scale_x_discrete(guide = guide_axis(n.dodge = 2)) +
  facet_wrap(~item,
             labeller = labeller(item = label_wrap_gen(32))
             ) +
  theme_rise() +
    labs(title = "Antal saknade svar över tid",
       subtitle = "Årskurs 9 (inkl. 'Vet ej')")

Code
df.comp %>% 
  mutater = factor(År),
         item = factor(item)) %>%
  mutate(item = fct_relevel(item, 'Jag trivs i skolan (TTS)',
                            "Jag trivs bra i skolan (STHLM)",
                            'De vuxna på skolan reagerar om de får reda på att någon har varit elak mot en elev (TTS)',
                            "Vuxna ingriper om någon blir trakasserad eller mobbad (STHLM)")) %>% 
  rename(Antal = n) %>% 
  filter(is.na(svarskategori)) %>% 
  ggplot(aes(x = År,
             y = Andel,
             color = Kön, group = Kön)) +
  geom_point(size = 3) +
  geom_line(linewidth = 1.1) +
  scale_color_gender() +
  scale_x_discrete(guide = guide_axis(n.dodge = 2)) +
  facet_wrap(~item,
             labeller = labeller(item = label_wrap_gen(32))
             ) +
  theme_rise() +
      labs(title = "Andel saknade svar över tid",
       subtitle = "Årskurs 9 (inkl. 'Vet ej')")

10.2 Antal svar över tid

Code
df.comp %>% 
  mutater = factor(År),
         item = factor(item)) %>%
  mutate(item = fct_relevel(item, 'Jag trivs i skolan (TTS)',
                            "Jag trivs bra i skolan (STHLM)",
                            'De vuxna på skolan reagerar om de får reda på att någon har varit elak mot en elev (TTS)',
                            "Vuxna ingriper om någon blir trakasserad eller mobbad (STHLM)")) %>% 
  rename(Antal = n) %>% 
  filter(!is.na(svarskategori)) %>% 
  group_by(År,Kön,item) %>% 
  summarise(TotalN = sum(Antal)) %>% 
  rename(Antal = TotalN) %>% 
  
  ggplot(aes(x = År,
             y = Antal,
             color = Kön, group = Kön)) +
  geom_point(size = 3) +
  geom_line(linewidth = 1.1) +
  scale_color_gender() +
  scale_x_discrete(guide = guide_axis(n.dodge = 2)) +
  facet_wrap(~item,
             labeller = labeller(item = label_wrap_gen(32))
             ) +
  theme_rise() +
  labs(title = "Antal svar över tid",
       subtitle = "Årskurs 9")

10.3 Svarsfördelningar

Code
ttsthlmComp <- function(svar) {
  df.comp %>% 
  mutate(item = factor(item)) %>%
  mutate(item = fct_relevel(item, 'Jag trivs i skolan (TTS)',
                            "Jag trivs bra i skolan (STHLM)",
                            'De vuxna på skolan reagerar om de får reda på att någon har varit elak mot en elev (TTS)',
                            "Vuxna ingriper om någon blir trakasserad eller mobbad (STHLM)")) %>% 
  rename(Antal = n) %>% 
  filter(svarskategori == {{ svar }}) %>% 
  
  ggplot(aes(x = År,
             y = Andel,
             color = Kön, group = Kön)) +
  geom_point(size = 3) +
  geom_line(linewidth = 1.1) +
  scale_color_gender() +
  scale_x_continuous(guide = guide_axis(n.dodge = 2),
                   limits = c(2016,2023),
                   breaks = c(2016:2023)) +
  facet_wrap(~item,
             labeller = labeller(item = label_wrap_gen(32)),
             scales = "free_x"
             ) +
  theme_rise() +
  labs(title = paste0(svar),
       subtitle = "Årskurs 9",
       y = "Andel i %")
}
Code
ttsthlmComp("Stämmer mycket bra") +
  ggtitle("Instämmer helt (TTS) / Stämmer mycket bra (STHLM)")

Code
ttsthlmComp("Stämmer ganska bra") +
  ggtitle("'3' (TTS) / Stämmer ganska bra (STHLM)")

Code
ttsthlmComp("Stämmer dåligt") +
  ggtitle("'2'+Instämmer inte alls (TTS) / Stämmer ganska+mycket dåligt (STHLM)")

Sammanslagna svarskategorier

Code
  df.comp %>% 
  mutate(#År = factor(År),
         item = factor(item)) %>%
  mutate(item = fct_relevel(item, 'Jag trivs i skolan (TTS)',
                            "Jag trivs bra i skolan (STHLM)",
                            'De vuxna på skolan reagerar om de får reda på att någon har varit elak mot en elev (TTS)',
                            "Vuxna ingriper om någon blir trakasserad eller mobbad (STHLM)")) %>% 
  rename(Antal = n) %>% 
  filter(svarskategori %in% c("Stämmer mycket bra","Stämmer ganska bra")) %>% 
  group_by(År,Kön,item) %>% 
  summarise(TotalN = sum(Andel)) %>% 
  rename(Andel = TotalN) %>% 
  
  ggplot(aes(x = År,
             y = Andel,
             color = Kön, group = Kön)) +
  geom_point(size = 3) +
  geom_line(linewidth = 1.1) +
  scale_color_gender() +
  scale_x_continuous(guide = guide_axis(n.dodge = 2),
                   limits = c(2016,2023),
                   breaks = c(2016:2023)) +  facet_wrap(~item,
             labeller = labeller(item = label_wrap_gen(32)),
             scales = "free_x"
             ) +
  theme_rise() +
  labs(title = "Stämmer bra",
       subtitle = "Årskurs 9",
       y = "Andel i %")